利用Angular本身ChangeDetectionStrategyAPI改变检查策略脱离变化检测器,即减少不必要的检测来提高应用的性能
zone是什么
对浏览器的异步api做了封装,并对外发出通知何时开始何时结束,angular在得到异步事件结束的通知后,执行变化检查。
zone性能优化的重点在哪
精确的控制哪些异步事件是应该在angular的zone以内运行的,哪些是应该在angular的zone之外运行的。显然在angular的zone之外运行的事件是不会进行变化检测的,减少不必要的变化检测则实现了性能上的优化
1 2 3 4 5 6 7
| import { NgZone } from '@angular/core'; constructor(private zone: NgZone) {} mouseDown(event) { this.zone.runOutsideAngular(() => { window.document.addEventListener('mouseover', this.mouseMove.bind(this)); // 此处应该尽量避免直接操纵dom,应该利用底层封装api(renderer, renderer2) }) }
|
NgZone可以让代码继续回到zone里运行,会再次触发anuglar的变化检测,调用NgZone.run();
1 2 3 4 5 6
| mouseUp(event) { this.zone.run(() => { ..... }) window.docuemnt.removeEventListener('mousemove', this.mouseMove); // 移除mousemove绑定的回调 }
|
补充
zone.js为javascript提供执行上下文,可以在异步任务之间进行持久性传递。采用了猴子补丁将javascript中的异步任务包裹了一层。使得异步任务运行在zone的上下文中。每一个异步任务都被当作一个task,并在task基础上提供钩子函数。
onZoneCreated: 产生一个新的zone对象时的钩子函数,zone.fork也会产生一个继承基类zone的新zone,形成一个独立的zone上下文
beforeTask: zone Task 执行前的钩子函数
afterTask
onError: zone运行Task时候的异常钩子函数
并且对大多数异步事件进行了包裹封装
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| const log = (phase) => { return () => { console.log('i am in zone.js' + phase + '!'); } } zone.fork({ onZoneCreated: log('onZoneCreated'), beforeTask: log('beforeTask'), afterTask: log('afterTask') }).run(() => { const methodLog = (func) => { return () => { console.log('i am from' + func + 'function') } }, foo = methodLog('foo'), bar = methodLog('bar'), baz = () => { setTimeout( methodLog('baz in setTimeout'), 0) }; foo(); baz(); bar(); })
|
1 2 3 4 5 6 7 8 9 10
| 输出结果: i am in zone.js beforeTask; i am from foo function; i am from bar function; i am in zone.js afterTask;
i am in zone.js onZoneCreated; i am in zone.js beforeTask; i am from baz in setTimeout function; i am in zone.js afterTask;
|
上述例子中将run方法分为了两个task,分别为同步task和异步task。fork方法会产生一个继承根zone的子类,并在fork函数中配置特定的钩子函数,形成独立的zone上下文,而run方法则是启动执行业务代码的对外接口。
使用Observable优化脏检查
使用OnPush的检查策略,如果修改了对象内部的值,此时不会进行脏检查,不会进行视图更新。此时可以选择Observable对象,通过手动调用markForCheck()方法进行优化,当前组件到根组件的路径上的所有组件都会进行变化检测。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @Component({ ..., changeDetection: ChangeDetectionStrategy.OnPush }) export class TodosComponent implements OnInit{ @Input() todos: Observable<Todo[]>; constructor(private cd: ChangeDetectorRef) {} ngOnInit() { this.todos.subscribe(todos => { 业务代码; this.cd.markForCheck(); }) } }
|