四、指令与管道:内置指令、自定义指令、纯管道与不纯管道、async管道
面试问答卡
面试问题
请解释 Angular 内置指令 *ngIf、*ngFor 和 *ngSwitch 的工作原理,并说明它们与自定义结构指令的区别。
面试官考察点
- 对模板编译和嵌入式视图的理解
- 结构指令的微语法(microsyntax)知识
- 自定义指令时对 ViewContainerRef 和 TemplateRef 的掌握
回答框架
- 明确分类:结构指令(改变 DOM 结构)vs 属性指令(改变外观/行为)
- 阐述 *ngIf 通过内嵌
+ 条件创建/销毁视图;*ngFor 通过 trackBy 优化性能 - 自定义结构指令需注入 TemplateRef 和 ViewContainerRef,在生命周期中调用 createEmbeddedView / clear
简短示例回答(约 90 字)
“*ngIf 将元素包裹于
可能追问
- “*ngIf 与 [hidden] 的性能差异?”
- “如何实现一个带 else 模板的 *ngIf?”
- “*ngFor 中 trackBy 如何减少 DOM 操作?”
面试问题
纯管道(pure pipe)和不纯管道(impure pipe)的区别是什么?在哪些场景下必须使用不纯管道?
面试官考察点
- 对 Angular 变更检测(Change Detection)的理解
- 纯函数概念在管道中的应用
- 性能意识:不纯管道对性能的影响
回答框架
- 定义:纯管道仅在输入参数变化时重新计算(对象引用变化);不纯管道每次变更检测周期都执行
- Angular 对纯管道的优化:复用上一次结果,减少不必要的计算
- 必须使用不纯管道的场景:管道内部依赖外部状态(如当前时间)、数组内容变化但引用不变、复杂对象内部属性变化且无法创建新引用
简短示例回答(约 80 字)
“纯管道基于输入引用的变化检测,例如 DatePipe 只对日期值变化响应。不纯管道在每次变更检测时执行,例如自定义 'timeAgo' 管道需显示实时相对时间。使用不纯管道必须加 'pure: false' 装饰器,并注意通过 memoization 或限制执行频率避免性能问题。”
可能追问
- “如何手动触发不纯管道重新计算?”
- “pure: false 对组件性能的影响有多大?”
- “为什么 async 管道被设计为不纯管道?”
面试问题
async 管道是如何实现自动订阅和取消订阅的?它与手动 subscribe/unsubscribe 相比有哪些优势?
面试官考察点
- 对 RxJS Observable 生命周期的掌握
- Angular 管道与组件销毁钩子的关联
- 内存泄漏防范意识
回答框架
- async 管道在内部调用 subscribe,将 Observable 值传递给模板
- 管道在 ngOnDestroy 钩子中调用 unsubscribe,防止内存泄漏
- 优势:代码简洁、自动管理生命周期、支持 OnPush 变更检测策略(通过 markForCheck)
- 局限:多次使用 async 管道可能导致多次订阅,推荐使用 shareReplay 或异步策略
简短示例回答(约 100 字)
“async 管道是一个不纯管道,它订阅传入的 Observable 或 Promise,在值变化时触发变更检测。当组件销毁时,管道在其 ngOnDestroy 中取消订阅。相比手动 subscribe,它消除了忘记取消的风险,且配合 OnPush 策略时自动调用 changeDetectorRef.markForCheck(),确保视图及时更新。缺点是每个模板中的 async 产生独立订阅,建议配合 shareReplay 优化。”
可能追问
- “async 管道在 OnPush 策略下如何工作?”
- “同一个 Observable 在模板多处使用 async,会订阅几次?”
- “async 管道如何处理 Promise 与 Observable 的区别?”
项目案例:电商后台权限管理系统
| 项目名称 | ECM 电商后台权限看板 |
| 业务背景 | 为运营团队提供角色化数据看板,不同角色(超级管理员、品类经理、客服主管)展示不同模块和操作按钮。要求前端根据后端返回的权限列表动态渲染菜单和按钮,且权限变更时实时响应。 |
| 规模指标 | 页面数 40+,指令数 15+(含自定义指令),管道数 8。活跃用户 500+,日均请求 2 万+。组件重用率 60%。 |
| 技术栈 | Angular 15、TypeScript、ngrx/store、RxJS、自定义指令 + 结构指令、async 管道、纯管道(日期、货币)、不纯管道(权限实时过滤) |
| 核心难点 |
|
| 面试高频追问 |
|
| 可迁移回答话术 |
“我在项目中使用
*appHasPermission 自定义结构指令,接收权限码字符串,内部注入 PermissionsService(返回 Observable),在 ngOnInit 中订阅并通过 ViewContainerRef 决定是否创建视图。配合 async 管道在父组件中订阅一次权限集合,通过 shareReplay(1) 避免子指令重复请求。指令销毁时自动取消订阅,确保了性能和内存安全。”
|
set 输入属性接收权限码并响应变化;谈到 async 管道时,补充 changeDetectorRef.detach() 与 reattach() 的手动控制。这些细节能让面试官看到你对底层变更检测的理解深度。