面试题答案
一键面试可能引发的性能问题
- 频繁的变更检测:大量组件嵌套且都有属性和事件绑定,Angular的变更检测机制会频繁触发,遍历整个组件树,检查每个组件的输入属性和绑定事件是否发生变化,这会消耗大量的CPU资源,导致性能下降。
- 不必要的组件更新:即使某个组件的实际数据并未改变,但由于变更检测机制的检查,仍可能触发该组件的更新,包括重新渲染DOM,浪费性能。
通过优化变更检测策略提高性能
- 实现思路:
- 默认变更检测策略:Angular默认采用
Default
变更检测策略,它会在每次事件循环时检查组件树中所有组件的状态变化。可以通过设置@Component
装饰器中的changeDetection
属性来改变变更检测策略。 - 自定义变更检测策略:对于那些状态变化不频繁的组件,可以手动调用
detectChanges
方法来进行变更检测。例如,在组件中注入ChangeDetectorRef
,然后在合适的时机(如数据真正发生变化时)调用this.cdRef.detectChanges()
。
- 默认变更检测策略:Angular默认采用
- 注意事项:
- 手动调用
detectChanges
需要准确把握数据变化的时机,否则可能导致数据更新不及时或不必要的更新。 - 过度使用手动变更检测可能使代码逻辑变得复杂,增加维护成本。
- 手动调用
使用OnPush策略提高性能
- 实现思路:
- 将组件的
changeDetection
策略设置为ChangeDetectionStrategy.OnPush
。当采用此策略时,组件只会在以下情况下触发变更检测:- 组件的输入属性引用发生变化(注意是引用变化,不是值变化)。例如,如果输入属性是一个对象,当对象本身被重新赋值(即引用改变)时才会触发变更检测。
- 组件接收到事件(如点击事件等)。
- 组件内部的Observable对象发出新值(前提是该Observable使用了
async
管道)。
- 对于输入属性为对象或数组的情况,确保传递新的对象或数组引用。比如,不要直接修改组件输入的数组,而是创建一个新的数组并传递。
- 将组件的
- 注意事项:
- 确保组件的输入属性是不可变的,否则可能无法触发变更检测。例如,对于对象,不要直接修改对象的属性,而是创建一个新的对象。
- 当使用
OnPush
策略时,如果在子组件中订阅了父组件传递的Observable,但没有使用async
管道,那么即使Observable发出新值,也不会触发子组件的变更检测,需要手动处理。
使用不可变数据结构提高性能
- 实现思路:
- 对象:使用
Object.assign
或展开运算符(...
)来创建新的对象。例如,要更新一个对象obj
的属性prop
:
- 对象:使用
const obj = { prop: 'oldValue' };
const newObj = {...obj, prop: 'newValue' };
- **数组**:使用数组的方法如`map`、`filter`、`concat`等返回新的数组,而不是直接修改原数组。例如,要向数组`arr`中添加一个元素:
const arr = [1, 2, 3];
const newArr = arr.concat(4);
- 这样在组件传递数据时,由于引用发生了变化,对于采用`OnPush`策略的组件,能正确触发变更检测。
2. 注意事项: - 不可变数据结构可能会增加内存使用,因为每次数据变化都创建新的对象或数组。但现代JavaScript引擎对此有一定优化。 - 确保整个应用的数据处理逻辑都遵循不可变数据结构的原则,否则可能导致变更检测不准确。