一窥Angular自动更新View的秘密——Zone.js
在使用angular框架时,变量会和视图模版绑定,一旦修改了变量,视图也随之改变
背后进行的是一种被称作“脏检查”的机制,所谓“脏检查”,其实就是新旧值的对比
改变的值可以看作“脏值”,angular会寻找所有绑定了“脏值”的组件,修改视图
这里不得不提的是angular1.0时,数据并非单向流动
所以有可能形成环形结构,触发多次“脏检查”,一直到变动的值稳定下来
有时甚至需要一些“黑魔法”,这也是1.0时性能被诟病的原因
但在2.0之后的版本,angular借鉴了react的单向数据流架构,不会再出现触发多次“脏值检测”的情况
那么,为什么我只是改动了数据,并没有调用类似change_detection之类的函数,angular就会自动触发“脏检查”呢
奥秘就在于zone.js这个类库
它是angular团队受Dart语言启发所开发的用于对异步操作进行包裹并暴露一些“钩子函数”的库
没错,正是因为zone.js对浏览器所有异步进行了包裹,才使得angular有机会把change_detection操作注入进去
这样只需要修改和视图绑定的变量,无需手动去声明要进行change_detection操作,angular就会自动进行“脏检查”
接下来不妨探究一下zone.js是怎么对异步包裹的
其实,它是采用“猴子补丁”的方式暴力对浏览器的所有异步进行了包裹
浏览器中的异步一般就是网络请求,事件,定时器这三类
以setTimeout为例,先把它保存起来
let temp = window.setTimeout;
然后去接管它
window.setTimeout = function(fn, time) { temp(function() { console.log('额外操作:定时器函数执行之前'); fn(); console.log('额外操作:定时器函数执行之后'); }, time); };
为了避免污染全局变量,可以放到自执行匿名函数中
;(function(_setTimeout) { window.setTimeout = function(fn, time) { _setTimeout(function() { console.log('额外操作:定时器函数执行之前'); fn(); console.log('额外操作:定时器函数执行之后'); }, time); }; })(window.setTimeout);
setTimeout包裹完成,可以像往常一样调用它试试
setTimeout(() => { console.log('我将在1.5s后执行'); }, 1500);
这时setTimeout指向的并非是浏览器提供的默认setTimeout函数,而是我们的自定义函数
zone.js正是通过这种方式对所有异步进行了包裹,并暴露出一些“钩子函数”,使原生API可以被注入一些自定义行为
对angular来说,它可以在这些“钩子函数”中,去进行新旧值的对比并对视图做出对应的改动
zone.js对日常开发带来的意义在于,它让浏览器中的异步变得可定制
当然,千万不要在angular中去对异步进行二次包裹(除非你非常清楚自己在做什么并且认为有必要这样做),
这极可能会覆盖angular本身对异步的第一次包裹,使程序产生不可预料的行为
最后,zone.js是一个独立的库,并非依赖于angular,你可以很轻松的使你的angular应用不依赖它
在项目文件的最外层,找到main.ts文件,改写配置
platformBrowserDynamic().bootstrapModule(AppModule, {ngZone: 'noop'})
.catch(err => console.error(err));
此时你的项目再运行看看,尽管数据更改了,但视图没有发生任何改变
如果想要视图更改的话,需要引入ApplicationRef,然后在构造函数中注入,接着在所有数据发生改变的地方调用tick方法触发“脏值检查”
import { Component, ApplicationRef } from '@angular/core';
constructor(private app: ApplicationRef) { }
this.app.tick();

浙公网安备 33010602011771号