一窥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();

 

posted @ 2020-11-07 15:26  吕洋  阅读(228)  评论(0)    收藏  举报