第3章 Angular核心概念与原理:组件、模块、依赖注入、变更检测
核心概念速查表
| 概念 | 定义 | 面试关键点 |
|---|---|---|
| 组件 | 视图+逻辑的封装单元,通过 @Component 装饰器定义 | 生命周期顺序、通信方式、样式封装策略 |
| 模块 | 用 NgModule 组织关联的组件/指令/管道/服务,管理编译上下文 | 模块作用域、懒加载、forRoot/forChild 模式 |
| 依赖注入 | 框架内置 DI 系统,通过注入器层级解析依赖实例 | providers 层级、providedIn 写法、@Optional/@Self/@SkipSelf |
| 变更检测 | 框架监听数据变化并同步更新视图的机制 | Default vs OnPush、ChangeDetectorRef、zone.js |
面试问答卡片
🎯 面试问题
请列举 Angular 组件的生命周期钩子,并说明执行顺序。
📌 考察点
- 是否熟悉组件从创建到销毁的全流程
- 对钩子触发条件的理解(view init / content init 的区别)
- 是否知道 ngDoCheck 与变更检测的关系
📝 回答框架
按执行顺序罗列钩子,并简释每个钩子的典型用途。强调 ngOnChanges 仅在 @Input 变化时调用;ngOnInit 在首次属性绑定后调用;ngDoCheck 自定义变化检测。
💬 示例回答
“构造函数 → ngOnChanges(多次) → ngOnInit(一次) → ngDoCheck(每次检测) → ngAfterContentInit → ngAfterContentChecked → ngAfterViewInit → ngAfterViewChecked → ngOnDestroy。最常用 ngOnInit 初始化数据,ngOnDestroy 取消订阅。”
➡️ 可能追问
- ngAfterViewInit 中操作 DOM 的注意事项
- 父组件和子组件生命周期钩子的先后顺序
- 为什么在 ngOnInit 里写逻辑而不在构造函数里写?
⚠️ 避坑提醒
不要混淆 Content 和 View 的钩子顺序;在构造函数中避免执行复杂操作或访问 @Input 属性(此时未赋值)。
🎯 面试问题
Angular 依赖注入的层级是如何划分的?解析服务的优先级是怎样的?
📌 考察点
- 是否理解注入器树与组件树的关系
- 能否清晰说明 providers 在 module / component 层级的区别
- 对 @Self / @SkipSelf / @Optional 等装饰器的掌握
📝 回答框架
先画注入器层次:根 → 模块 → 组件(层级)。解析时由当前组件注入器向上查找,直到根注入器。提供例子说明不同层级 providers 覆盖规则。
💬 示例回答
“四级注入器:根(environmentInjector) → 平台 → 模块(NgModule) → 组件(@Component)。查找时从当前组件注入器开始,如果找不到向上找父组件注入器,再到模块注入器,最后到根。常用 providedIn: 'root' 实现单例。”
➡️ 可能追问
- 懒加载模块的注入器与主模块的关系
- 如何在一个组件里强制使用父级注入器的实例?
- providedIn: 'any' 的效果是什么?
⚠️ 避坑提醒
懒加载模块会创建独立的注入器,此时再往模块 providers 注册会生成新实例,可能导致状态不一致。推荐将共享服务放在核心模块或 providedIn: 'root'。
🎯 面试问题
Angular 的变更检测策略有哪些?如何选择 Default 和 OnPush?
📌 考察点
- 是否理解 zone.js 如何触发变更检测
- OnPush 策略的触发条件和适用场景
- 是否掌握手动调用 ChangeDetectorRef.markForCheck() 的方法
📝 回答框架
先对比两种策略:Default 每次异步事件都从根遍历所有组件;OnPush 只检查输入属性引用变化、组件内事件、手动触发。然后说明性能优化时优先 OnPush + 不可变数据。
💬 示例回答
“Default 策略全量检测,适合小型应用。OnPush 只检测输入引用变化或事件触发的组件,配合 Immutable.js 或纯函数可大幅减少性能消耗。如果组件内部 Observable 更新,需调用 markForCheck 让父级知晓。”
➡️ 可能追问
- OnPush 下异步管道为什么能正常更新?
- ChangeDetectorRef.detach() 和 detach() 的区别
- 如何排查变更检测性能瓶颈?
⚠️ 避坑提醒
OnPush 策略下修改对象的内部属性不会触发检测(引用不变),必须创建新引用。不要在所有组件上滥用 OnPush,会导致复杂的 markForCheck 调用链。
🏗️ 项目案例章节:企业级低代码配置平台
| 项目名称 | 企业级低代码配置平台(Angular 前端) |
| 业务背景 | 解决业务部门重复开发 CRUD 页面的痛点,通过拖拽组件 + JSON Schema 动态生成表单和列表页面,降低开发成本 60% |
| 规模指标 | 50+ 业务页面组件,20+ 功能模块,10+ 微前端子应用,30 万行 TypeScript 代码 |
| 技术栈 | Angular 14+ / NgRx / RxJS / Angular Material / Nx Monorepo / Webpack Module Federation |
| 核心难点 | 1. 复杂表单动态渲染(异步校验、级联联动) 2. 变更检测性能优化(大型表格实时更新数据) 3. 微前端共享注入器与路由协作 |
| 面试高频追问 |
|
| 可迁移回答话术 | “我们在项目中通过 Nx 构建 monorepo,使用 shared lib 管理公共服务。性能方面,对高频更新的表格页设置 OnPush + trackBy,并配合 ChangeDetectorRef.markForCheck 手动控制。微前端使用 Module Federation 并注入共享的 AuthService 实例,通过 避免跨子应用状态污染。” |