很多前端开发者第一次接触 Angular 时感受通常不是好用而是劝退。项目目录层层叠叠Module、Component、Service、Directive、Pipe 一堆概念同时扑过来还没写出第一行业务代码光是理解 CLI 生成的项目结构就要花掉半天。但另一边在大型企业级项目、中后台管理系统、金融政务类应用的选型讨论里Angular 依然是一个绕不开的名字。它不靠热度取胜因为它解决的问题和 React、Vue 并不在同一个层面。这篇文章想做的事情很明确把 Angular 从环境搭建到核心开发、从面试高频点到工程化最佳实践、从与 Vue 的选型对比到常见排错串成一条完整的知识链路。读完你不仅能跑通一个 Angular 项目还能理解它为什么这样设计、适合解决什么问题、在真实项目里应该怎么用。如果你正准备学 Angular、要接手 Angular 项目或者正在做技术选型这篇内容可以直接作为你的路线图。1. Angular 到底解决了什么问题先下一个判断Angular 不是给快速写个页面的人准备的它是给要把一个项目维护五到十年的团队准备的。1.1 前端项目规模失控的痛点当你做一个活动页、一个小型后台时React 和 Vue 的灵活优势非常明显组件随便拆状态管理按需加几乎没有约束。但项目一旦变大参与人数一旦变多问题就来了每个人代码风格不一样组件命名天花乱坠很难统一约束。状态管理方案五花八门不同模块各写一套交接成本极高。依赖升级时破坏性变更难以排查缺少统一的依赖管理机制。新人进来后光理解项目的技术选型和数据流就要花几周。Angular 的答案和 React、Vue 不一样。它不走灵活组合路线而是直接给出一套完整的工程框架一套 CLI 工具、一套模块化方案、一套依赖注入机制、一套内置 HTTP 和路由、一套 RxJS 异步模型。1.2 Angular 的设计哲学Angular 的设计哲学可以概括为约定优于配置。它通过框架层面的约束把团队成员的技术选择统一起来。你用 Angular 写项目不需要反复争论Router 用哪个、HTTP 库用哪个、表单方案选哪个——框架已经在官方层面给出了标准方案。这对小型项目来说是束缚但对大型团队来说反而是一种保护。因为技术选型的决定权从个人偏好转移到了框架统一规范之下。这也是为什么 Angular 在大型企业级项目里始终保有一席之地。1.3 Angular 真正降低的成本从长期项目运营的角度看Angular 降低的是三类成本协作成本所有团队成员遵循同一套规范代码审查更省力。维护成本依赖注入 模块化让代码的可测试性天然很高单元测试容易写。交接成本Angular 项目结构高度一致新成员只要懂 Angular 基本概念就能快速定位问题。但它也有代价学习曲线明显比 Vue 和 React 陡峭第一次上手时的挫败感很强。这是所有 Angular 学习者必须正视的第一件事。2. Angular 核心概念与架构理解Angular 的概念多但核心骨架并不复杂。你只需要抓住六根主线组件、模板、指令、服务与依赖注入、路由、RxJS。2.1 组件Angular 的最小单元在 Angular 中组件是 UI 构建的最小单元几乎每个页面都由组件树组合而成。一个组件由三部分组成带有Component装饰器的 TypeScript 类负责业务逻辑和数据。HTML 模板负责界面结构。可选的样式文件负责外观。下面是最基本的组件代码// 文件路径src/app/app.component.ts import { Component } from angular/core; Component({ selector: app-root, templateUrl: ./app.component.html, styleUrls: [./app.component.css] }) export class AppComponent { title angular-demo; }selector定义了组件在模板中的标签名templateUrl指向模板文件。你在 index.html 或父组件模板里写app-root/app-rootAngular 就会把这个组件渲染到对应的位置。2.2 模板与数据绑定Angular 模板支持四种常见的绑定方式绑定类型语法作用插值绑定{{ title }}把组件属性渲染到模板属性绑定[disabled]isDisabled把组件属性绑定到 DOM 属性事件绑定(click)handleClick()监听 DOM 事件触发组件方法双向绑定[(ngModel)]username表单输入和数据同步需要注意属性绑定的方向是从组件到模板事件绑定是从模板到组件双向绑定是两者结合。新手最容易搞混的是[属性]和(事件)的方向记住方括号是进、圆括号是出就清晰了。2.3 服务与依赖注入服务是 Angular 中复用业务逻辑和数据的载体依赖注入则是把服务实例自动送进组件的机制。在没有依赖注入的框架里你需要手动new一个服务对象还要管理它的生命周期。在 Angular 中你只需要在构造函数里声明参数类型Angular 会自动帮你创建并注入实例import { Injectable } from angular/core; Injectable({ providedIn: root }) export class UserService { getUsers() { return [张三, 李四, 王五]; } }import { Component } from angular/core; import { UserService } from ./user.service; Component({ selector: app-user-list, template: ul li *ngForlet user of users{{ user }}/li /ul }) export class UserListComponent { users: string[]; constructor(private userService: UserService) { this.users this.userService.getUsers(); } }providedIn: root表示该服务在根注入器中提供服务整个应用共享一个实例。这样做的好处是组件只关心我需要什么服务完全不关心这个服务是怎么创建出来的。2.4 模块与 standalone 组件早期 Angular 版本中NgModule 是组织代码的核心方式。每个功能都需要声明在某个模块里。从 Angular 15 开始Angular 引入了 standalone 组件允许组件不依赖 NgModule 独立使用到 Angular 17standalone 已成为新项目的默认方式。用ng new创建的新项目已经不再自动生成app.module.ts而是在app.config.ts中集中配置。这一变化大幅简化了学习路径也减少了样板代码。如果你接手的是老版本项目可能仍然见到大量app.module.ts和declarations数组理解两者的差异是迁移和升级的前提。2.5 RxJSAngular 的异步编程基础设施RxJS 是一套基于观察者模式的响应式编程库也是 Angular 中最被新人痛恨、又绕不开的部分。简单理解Observable是一个可以被订阅的数据流它和 Promise 最大的区别是——Promise 只能返回一个结果而 Observable 可以持续发射多个值。实际项目中HTTP 请求、表单值变化、路由参数变化都可以用 Observable 表达。配合async管道模板可以直接订阅数据流p{{ userList$ | async }}/pRxJS 是 Angular 的深度依赖不理解它就看不懂 Angular 导航事件、表单控件甚至 HTTP 响应处理的内部逻辑。但初学不需要太深入操作符先理解数据流这个抽象就够了。3. Angular 环境准备与安装配置Angular 的安装核心是 Angular CLI。CLI 是 Angular 官方提供的命令行工具承担创建项目、生成代码、构建、测试、部署等几乎所有工程化任务。3.1 前置条件安装 Angular CLI 之前需要先确认本机环境软件建议版本Node.js建议使用当前 LTS长期支持版本具体版本以 Angular 官方文档要求为准npmNode.js 自带建议使用较新版本IDEVisual Studio Code Angular Language Service 扩展版本提示不同的 Angular CLI 版本对 Node.js 版本有不同要求。安装前先执行node -v查看本机版本如果版本过低建议先通过官方安装包或 nvm 升级 Node.js再继续后续步骤否则极易在npm install阶段报错。3.2 安装 Angular CLI打开终端执行全局安装命令npm install -g angular/cli安装完成后验证版本ng version如果能看到类似下面的输出说明安装成功Angular CLI: 18.x.x Node: 20.x.x Package Manager: npm 10.x.x3.3 npm 镜像配置建议国内网络环境下npm install经常因为访问官方 registry 过慢而失败。建议将 registry 切换为国内镜像npm config set registry https://registry.npmmirror.com验证是否生效npm config get registry切换镜像后重新执行npm install -g angular/cli安装速度通常会快很多。这里提醒一句如果你在公司内网环境npm 镜像可能由公司统一配置操作前先确认团队规范避免影响现有项目。4. 从零创建并运行第一个 Angular 项目4.1 创建项目执行以下命令创建一个名为angular-demo的项目ng new angular-demo执行过程中 CLI 会问你几个问题是否要添加 Angular routing输入y项目会自带路由配置。选择样式格式默认推荐 CSS也可以选择 SCSS按团队习惯来。生成完成后进入项目目录cd angular-demo4.2 启动本地开发服务器ng serve启动成功后终端会显示✔ Browser application bundle generation complete. Initial chunk files | Names | Raw size ... Application bundle generation complete. [2.932 seconds]打开浏览器访问http://localhost:4200就能看到 Angular 的默认欢迎页面。ng serve启动的是带热更新HMR的开发服务器修改代码保存后浏览器会自动刷新这也就是热更新最简单的验证方式。4.3 认识项目目录结构ng new生成的关键目录和文件如下路径作用src/app/应用的主代码目录所有业务组件都在这里src/main.ts应用入口文件负责启动 Angular 应用src/index.html页面入口 HTMLangular.jsonAngular 项目的工程配置tsconfig.jsonTypeScript 编译配置package.jsonnpm 依赖管理文件如果你遇到app.module.ts说明项目用的是传统 NgModule 模式如果看到的是app.config.ts说明是新的 standalone 模式。两种模式在新版本中都能正常使用但新项目建议优先使用 standalone。5. Angular 核心开发完整示例下面用一个用户管理的小功能把组件、数据绑定、路由、服务和 HTTP 请求串起来。这个示例覆盖了实际项目中最常见的开发路径。5.1 生成子组件先创建一个名为user-list的组件ng generate component user-list执行后CLI 会生成四个文件src/app/user-list/user-list.component.ts src/app/user-list/user-list.component.html src/app/user-list/user-list.component.css src/app/user-list/user-list.component.spec.ts5.2 修改根组件模板修改app.component.html把根组件改造成一个简单导航页面!-- 文件路径src/app/app.component.html -- h1Angular 用户管理示例/h1 nav a routerLink/users用户列表/a /nav router-outlet/router-outletrouter-outlet是路由出口Angular 会把匹配的路由组件渲染到这个位置。5.3 配置路由修改路由配置文件app.routes.ts// 文件路径src/app/app.routes.ts import { Routes } from angular/router; import { UserListComponent } from ./user-list/user-list.component; export const routes: Routes [ { path: , redirectTo: /users, pathMatch: full }, { path: users, component: UserListComponent } ];路由配置的意思是访问/时重定向到/users访问/users时渲染UserListComponent。5.4 编写服务模拟数据接口在真实项目中用户数据通常来自后端 HTTP 接口。这里先创建一个服务用模拟数据演示ng generate service services/user修改服务文件// 文件路径src/app/services/user.service.ts import { Injectable } from angular/core; import { Observable, of } from rxjs; export interface User { id: number; name: string; role: string; } Injectable({ providedIn: root }) export class UserService { private mockUsers: User[] [ { id: 1, name: 张三, role: 管理员 }, { id: 2, name: 李四, role: 普通用户 }, { id: 3, name: 王五, role: 编辑 } ]; getUsers(): ObservableUser[] { return of(this.mockUsers); } }这里返回的是ObservableUser[]而不是数组本身因为真实接口中HTTP 请求返回的就是 Observable 数据流。先用of模拟之后替换成HttpClient时调用方的代码基本不用改。5.5 在组件中使用服务和数据绑定修改UserListComponent的 TypeScript 文件// 文件路径src/app/user-list/user-list.component.ts import { Component, OnInit } from angular/core; import { CommonModule } from angular/common; import { UserService, User } from ../services/user.service; Component({ selector: app-user-list, standalone: true, imports: [CommonModule], template: h2用户列表/h2 table thead tr thID/th th姓名/th th角色/th /tr /thead tbody tr *ngForlet user of users td{{ user.id }}/td td{{ user.name }}/td td{{ user.role }}/td /tr /tbody /table , styles: [ table { border-collapse: collapse; width: 100%; } th, td { border: 1px solid #ccc; padding: 8px; } ] }) export class UserListComponent implements OnInit { users: User[] []; constructor(private userService: UserService) {} ngOnInit(): void { this.userService.getUsers().subscribe(data { this.users data; }); } }这段代码涉及三个 Angular 核心机制onInit生命周期钩子组件初始化完成后自动调用适合在这里加载数据。构造函数注入UserService不手动new而是让 Angular 提供实例。*ngFor结构指令循环渲染数组数据到模板中。5.6 接入真实 HTTP 请求当后端接口就绪后把服务改成真正的 HTTP 请求// 文件路径src/app/services/user.service.ts import { Injectable } from angular/core; import { HttpClient } from angular/common/http; import { Observable } from rxjs; import { User } from ./user.service; Injectable({ providedIn: root }) export class UserService { private apiUrl /api/users; constructor(private http: HttpClient) {} getUsers(): ObservableUser[] { return this.http.getUser[](this.apiUrl); } }需要在使用HttpClient的组件配置中引入提供者。在 standalone 模式下通常在app.config.ts中添加// 文件路径src/app/app.config.ts import { ApplicationConfig, provideZoneChangeDetection } from angular/core; import { provideRouter } from angular/router; import { provideHttpClient } from angular/common/http; import { routes } from ./app.routes; export const appConfig: ApplicationConfig { providers: [ provideZoneChangeDetection({ eventCoalescing: true }), provideRouter(routes), provideHttpClient() ] };provideHttpClient()是 Angular 新版本中注册 HTTP 客户端的方式。如果使用HttpClient却不注册提供者运行时会直接报NullInjectorError: No provider for HttpClient的错误这个报错在搜索 Angular 问题时非常高频。6. 运行结果与效果验证6.1 开发环境验证确保项目还在运行状态如果已关闭重新执行ng serve访问http://localhost:4200页面应该显示标题Angular 用户管理示例。自动跳转到/users路径。显示三行用户数据张三、李四、王五。如果在浏览器中看到表格和数据说明组件、路由、依赖注入、数据绑定这一整套链路已经跑通。6.2 构建生产包开发完成后的标准方式ng build构建成功后你会看到输出目录dist/angular-demo/里面是打包后的静态文件。可以把该目录部署到 Nginx、CDN 或对象存储上。构建结果示例Initial chunk files | Names | Raw size | Estimated transfer size main.js | main | 203.55 kB | 54.50 kB ... ✔ Complete output written to: dist/angular-demo6.3 判断构建成功的标准如果出现Complete output written to说明构建成功。如果构建失败最可能是 TypeScript 类型错误或模板语法错误。此时优先看终端日志中红色报错部分它通常会直接指出是哪个文件、哪一行出了问题。6.4 单元测试验证Angular 项目默认集成了 Jasmine 和 Karma新版中也有 Jest 方案基础测试命令是ng test测试是 Angular 工程化优势的体现因为依赖注入天然让组件和服务解耦写单元测试时只需要替换 Mock 服务即可。这一点在企业项目里非常加分也是 Angular 面试和实际开发中都重要的能力。7. Angular 面试重点串讲Angular 相关的高频面试题其实非常集中在几个固定方向上。如果准备面试优先把下面这些内容吃透。7.1 Angular 和 AngularJS 的区别这是最基础的入门题。AngularJS 指的是 1.x 版本双向绑定、指令等概念在它身上成熟但 Angular 2 是一次完全重写不再是简单升级。维度AngularJS (1.x)Angular (2)语言JavaScriptTypeScript架构基于控制器基于组件性能脏检查机制变更检测优化移动支持较弱原生支持7.2 组件生命周期钩子Angular 组件的生命周期按顺序有多个钩子最常用的是钩子触发时机常见用途ngOnInit组件初始化完成后请求数据、初始化逻辑ngOnChanges输入属性变化时响应外部输入变化ngAfterViewInit视图渲染完成后操作 DOMngOnDestroy组件销毁前取消订阅、清理资源面试时最常踩坑的问题异步订阅了 Observable组件销毁了怎么办正确答案是在ngOnDestroy中取消订阅防止内存泄漏。7.3 依赖注入面试官高频追问点Angular 依赖注入有什么好处核心答案就三点解耦组件不关心服务的创建细节。可测试测试中可以轻松替换 Mock 服务。生命周期管理Angular 统一管理服务实例。7.4 Observable 与 Promise 的区别这也是必考题。核心区别Promise 只能发射一个值Observable 可以发射多个值。Observable 支持取消订阅Promise 一旦发出请求无法取消。Observable 是一系列操作的核心抽象配合 RxJS 操作符可以组合复杂异步逻辑。技术表达上可以说Observable 是懒执行的数据流因为只有订阅了才会开始发射数据。7.5 路由守卫路由守卫用于控制用户能否进入某个路由。常见守卫包括CanActivate能否进入。CanDeactivate能否离开。Resolve进入路由前预取数据。实际项目中权限控制基本都靠CanActivate实现。比如未登录用户访问后台页面时守卫判断用户状态后重定向到登录页。8. Angular 与 Vue 场景化选型对比Angular 和 Vue 哪个好是社区里永远讨论不完的问题但选型从来不是比谁更优秀而是比谁更适合当前场景。8.1 设计哲学对比维度AngularVue定位一站式完整框架渐进式灵活框架语言强制 TypeScript可选 TypeScript数据流依赖注入 RxJS响应式系统更直观工程化CLI 内置全套方案脚手架灵活方案可拼装学习曲线较陡平缓8.2 适用场景建议适合选择 Angular 的情况大型企业级中后台系统模块多、团队大。团队有较强 TypeScript 基础愿意接受框架约束。项目要求长期维护、高可测试性。业务逻辑复杂需要依赖 RxJS 处理大量异步数据流。适合选择 Vue 的情况中小型项目追求快速上线。团队偏原生前端背景不想被框架严格约束。需要灵活接入外部生态库按需组合。8.3 工程化差异Angular 的价值不在语法层面而在工程层面。它有统一的目录结构、统一的依赖注入机制、统一的构建工具。Vue 更像乐高积木官方提供框架核心别的由你去拼装。如果你讨厌做技术选型的选择题Angular 会舒服很多如果你喜欢掌控一切、按需组装Vue 会更有吸引力。8.4 最容易被忽略的选型因素选型时除了技术本身还要看团队背景和时间成本。Angular 的上手周期明显长于 Vue如果你的项目排期非常紧张、团队又全是 Vue 开发经验贸然切换 Angular 会带来短期效率下降的阵痛。反之如果项目需要五年期持续迭代、模块纵横交错前期多投入的学习成本是值得的。9. 常见问题与排查思路Angular 开发中的报错非常多但很多问题的排查路径是固定的。下表是几个高频问题问题现象可能原因排查方式解决方案ng serve启动失败端口被占用4200 端口被其他程序占用lsof -i :4200macOS/Linux换端口启动ng serve --port 4300Node.js 版本过低导致安装失败本机 Node 版本不满足 CLI 要求node -v查看版本升级 Node.js 到 LTS 版本npm install很慢或超时默认 registry 访问慢npm config get registry切换为国内镜像源编译报错找不到模块依赖未安装完整查看node_modules是否存在删除node_modules后重新npm installNullInjectorError: No provider for HttpClient未注册 HTTP 客户端检查app.config.ts添加provideHttpClient()路由跳转后页面空白路由出口缺失或配置错误检查模板是否有router-outlet确认根模板包含路由出口ng build后部署访问 404服务器未配置 SPA 回退检查 Nginx 配置配置 try_files 回退到 index.html9.1 端口被占用处理如果ng serve提示端口占用最简单的方式是指定新端口ng serve --port 43009.2 依赖冲突处理Angular 项目对依赖版本有严格要求。当出现奇怪的编译错误时优先执行npm install如果依然报错可以采用干净重装流程rm -rf node_modules package-lock.json npm install注意删除node_modules前建议先确认项目是否需要备份锁文件。package-lock.json用于锁定依赖版本删除后重新生成的版本可能有差异提交到 Git 时会产生大量变更。9.3 模板报错定位技巧Angular 的模板编译错误信息通常比较友好会直接指出模板文件和标签位置。遇到模板报错先检查标签是否闭合。指令名称是否拼写正确。绑定的组件属性是否存在。是否遗漏了CommonModule导入。9.4 安全提醒在使用ng serve进行开发调试时默认开启的开发服务器仅限本地使用如果需要局域网内联调可以将 server 绑定到指定 IP。但注意不要在生产环境直接使用ng serve生产环境必须通过ng build构建静态资源后部署并由 Web 服务器统一管理静态文件和路由回退。任何涉及生产环境变更的操作都应先在小流量或测试环境验证并做好备份与回滚方案。10. 生产环境最佳实践与工程建议10.1 目录结构规范随着项目增长建议按业务域拆分目录而不是按技术类型平铺src/app/ modules/ user/ components/ services/ models/ order/ components/ services/ models/ shared/ components/ directives/ pipes/这种按业务聚合的结构比所有组件放一起、所有服务放一起更适合大型团队协作。10.2 组件粒度控制一个组件的模板如果超过 200 行通常就该考虑拆分。Angular 组件应该是小而专注的每个组件只做一件事。把通用逻辑抽到服务里把频繁复用的 UI 片段抽成子组件。这样既方便测试也方便后续维护。10.3 RxJS 使用规范在实际项目中记住一条核心规范谁订阅谁负责取消。在组件中订阅 Observable 后正确做法是把Subscription保存下来在ngOnDestroy中统一取消import { Component, OnDestroy, OnInit } from angular/core; import { Subscription } from rxjs; import { UserService, User } from ../services/user.service; Component({ selector: app-user-list, standalone: true, template: ... }) export class UserListComponent implements OnInit, OnDestroy { users: User[] []; private subscription: Subscription; constructor(private userService: UserService) {} ngOnInit(): void { this.subscription this.userService.getUsers().subscribe(data { this.users data; }); } ngOnDestroy(): void { this.subscription.unsubscribe(); } }不过更推荐的做法是使用 Angular 内置的AsyncPipe在模板中直接订阅数据流Angular 会在组件销毁时自动取消订阅从而彻底避免手动管理订阅的麻烦。10.4 使用惰性加载大型项目中路由应该按模块启用惰性加载避免首屏加载全部代码。Angular 路由支持路由级代码分割配合loadComponent或loadChildren实现按需加载// 文件路径src/app/app.routes.ts import { Routes } from angular/router; export const routes: Routes [ { path: users, loadComponent: () import(./user-list/user-list.component).then( (m) m.UserListComponent ) } ];这样/users的组件代码会独立成一个 chunk只有访问该路由时才加载能显著减小首屏体积。10.5 变更检测策略Angular 默认每次事件都会跑全量变更检测。对于大型列表或频繁更新的组件可以改用ChangeDetectionStrategy.OnPush策略只在输入引用变化或手动触发时检查import { ChangeDetectionStrategy, Component } from angular/core; Component({ selector: app-demo, standalone: true, template: ..., changeDetection: ChangeDetectionStrategy.OnPush }) export class DemoComponent {}但要注意OnPush 不是银弹。它要求你遵循不可变数据流的习惯如果到处修改对象内部属性而不改变引用视图可能不更新排错时会很困惑。10.6 安全边界强调Angular 内置了对 XSS 的防护默认会转义模板中的用户输入内容。但在实际项目中仍需注意不要把未经过滤的 HTML 直接用innerHTML绑定除非你非常清楚内容来源。涉及权限控制的功能服务端必须做二次校验前端控制只是体验优化。尽量遵循最小权限原则服务、接口、角色分配都按实际需要开放不要图省事放开全部权限。10.7 版本升级建议Angular 的版本迭代速度较快官方通常每年发布一个大版本。做升级前务必查看官方升级指南。在测试环境先升级验证。评估弃用 API 的影响。升级后跑完整测试套件。不要在生产环境直接跨版本升级。Angular 团队提供了自动化迁移工具利用好它可以减少大量手工修改工作。10.8 团队协作规范约定几个实用的团队规范所有组件都通过 CLI 生成保证代码模板一致。组件 selector 使用统一前缀避免和第三方组件冲突。提交代码前必须跑一遍构建和单测。公共服务和共享组件必须有 JSDoc 注释说明用途和示例。11. 总结Angular 的真正门槛不在语法而在思维方式。它要求你先理解组件树、依赖注入、数据流、模块化这些工程概念然后才能顺畅地写业务代码。但也正是这些概念让 Angular 在大规模、长期维护的项目中展现出非常强的工程优势。如果你是从零开始学建议按这个顺序往下走用 CLI 跑通一个最小项目。写一个带路由、服务、HTTP 请求的小功能。深入理解依赖注入和 RxJS。熟悉常见生命周期钩子和变更检测机制。用 Angular 的测试工具给核心服务写几个单元测试。在生产项目中实践惰性加载、OnPush 策略和目录规范。如果你是在选型记住一个关键判断Angular 是框架约束换长期可维护性Vue 是灵活自由换快速迭代。两者没有优劣之分只有场景匹配度。真正的高手不会纠结于框架之争而是清楚每种选择的代价在哪里。本文核心内容建议收藏备用尤其是环境搭建、路由配置和常见问题排查几张表实际开发中会经常回来查。接下来你可以打开终端执行ng new demo亲手把第一个 Angular 项目跑起来。跑通的那一刻你会意识到之前觉得天书一样的概念其实并没有那么可怕。