Angular 状态检测机制
Angular 通过对数据的变更检测来实现数据对视图的双向绑定,那么这篇文章将会讲述如何通过内置功能来优化状态检测机制。
变更检测机制
为了理解变更检测机制,需要知道这么一些知识。
值类型和引用类型
所谓值类型,意味着它只会把值放在当前的栈中,而引用类型,则意味着它把值放在堆内存中。他们俩的区别在于,值类型的每次赋值操作都会拷贝一个新的值,而引用类型的赋值操作仅仅会拷贝堆地址而给新值,而不是重新拷贝一份新的数据给新值。而 JavaScript 中,这两个类型的运行方式跟上面类似,但实现方式不同。
数据变化
异步操作会触发变更检测,他们可以分为这么些类型:
- 用户输入等事件
- 服务端请求
- 定时事件
如何通知变化
如果想要知道如何通知变化,那么就得了解Zone.js。
Zone.js 通过重写所有的异步API来对它们进行统一的管理,那么 Angular 就能通过它来对所有的异步操作进行监听,如果应用执行了异步操作,那么 Zone.js 就会通知 Angular 此处可能会有数据变化,需要进行变更检测(也就是 Angular 会发生变化的组件标记为dirty)。当 MicroTask 栈空的时候,Angular 就会执行 ApplicationRef.tick 对应用进行变更检测。
检测策略
Angular 提供了两种不同的检测策略,默认和 OnPush。
一个组件可以通过输入 @Input 的形式来获取输入,那么不同的策略就会根据输入来做出不同的决策。
默认情况
这个组件获取的 @Input 无论是值类型还是引用类型,只要改变了,都会触发变更检测。对于引用类型,改变了这个引用的属性也会触发。
OnPush
对于值类型,只要改变了,就会触发变更检测。对于引用类型,如果只改变属性,不会触发变更检测。只有整个引用变了,才会触发变更检测。
OnPush策略详解
尽管 Angular 很快,但是,在大规模的应用下,如果每次的简单事件都触发大量的变更检测,这便会有很明显的性能问题。所以,为了解决这个问题,使用 OnPush 模式成为了非常必要的选择。
这个策略的主要解释是,在单向数据流的情况下,组件视图变化只会根据外部输入的数据来变化。 理解了这句话,那么就能理解 OnPush 的逻辑。
那么如何使用 OnPush 来优化状态检测呢,可以看以下代码:
typescript
这样,在组件的注解中标明变更检测机制为 OnPush,就能开启 OnPush 策略。
ChangeDetectorRef
ChangeDetectorRef 是组件变更检测器的引用,在组件中,我们可以通过依赖注入的方式来获取该对象。
typescript
ChangeDetectorRef 主要有以下几个方法:
typescript
首先,detach 和 reattach 是用来对解除或者重新绑定当前视图的。原理很简单,就是将当前视图的状态标记为解除,当变更检测进行的时候,判断视图的检测状态。
其次,对于 markForCheck 和 detectChanges 两个API,在OnPush的策略下,组件内部可能还会有外部的用户事件输入,那么这种变化是不触发状态检测,因为不是通过 @Input 输入,那么我们需要通过标记检测来进行视图更新,这里用了官网的例子:
typescript
至于其他用法,可以参考官网。