Zone.js 入门

Photo by Sebastian Unrau on Unsplash

声明

本文是 Zone.js 源码解析的前置章节。在这里,我会简单介绍 Zone.js 要解决

的问题、简单原理以及基本用法。我相信这是我们探索源码前的必备工作。假

如您已经有了 Zone.js 的相关应用经验,就可以轻松加愉快的直接略过本章,

和我一起来探索 Zone.js 的具体实现。

Zone.js 要解决的问题

生命周期钩子

在某些场景下,我们希望浏览器环境下的原生异步函数内置生命周期钩子,让

我们可以在更细的粒度上操作控制它们。比如在 Angular 中,我们就可以利用

生命周期钩子,在原生异步函数执行、事件产生后执行变更(脏值)检测。

代码隔离

代码隔离本质上是生命周期钩子衍生出来的问题。试想,假如我们对全局

setTimeout 利用生命周期钩子进行了改造,那么在任何地方调用它都会触发该

生命周期钩子,这势必会引入新的问题。因此在添加钩子时,还需要有一套机

制来隔离生命周期钩子的作用区域。开发者可以在各个独立的区域下,针对不

同需求,设定不同的生命周期钩子的具体实现,且区域之间同名函数互不影

响。

Zone.js 实现原理

Zone.js 引入了上面提到的生命周期钩子与代码隔离的概念。在 Zone.js 在引入

时,它会对浏览器环境下的异步函数、事件、Promise 做一个替换(Monkey-

Patch),将其构造成特殊的函数。这种函数,能够由 Zone.js 进行统一管理,

实现在不同区域(Zone)下的隔离。

Zone.js 的基本用法

Zone.js 在载入时帮我们把浏览器环境下对内置的异步函数、事件、Promise

做了替换,在实际使用中,我们只需关注如何利用 Zone.js 进行代码隔离和钩

子配置。

普通 fork

Zone.js 通过 fork 创建区域(Zone)的方式来隔离配置。通过 fork 的方法,

我们不仅能够创建彼此独立的区域,还能实现区域的嵌套,实现区域的关联。

如上所示,我们通过 fork 创建了一个名为 ZoneA 的区域,并配置了若干钩

子。需要注意的是, Zone.js 提供了一个全局对象 Zone,我们的任何配置都要

基于它来执行。Zone中成员变量 current 始终指向代表当前区域的 Zone。

链式 fork

由于 fork 返回的是一个新的 Zone 对象,假如我们再此基础上链式调用 fork,

就能实现区域的嵌套。如下所示,我们在创建完 ZoneA 后继续 fork 一个区域

ZoneA1 。那么, ZoneA1 中的代码在调用完自身区域中的钩子配置后,可以

选择是继续调用 ZoneA 的钩子配置,还是覆盖其配置。

当然你也可以这样写

在区域中运行代码

配置完区域和钩子之后,我们就可以在区域中运行我们的代码。通过将运行代

码传入 Zone 对象的 run 方法,设定的钩子函数就会在特定阶段自动执行。

比如这里,我们在 ZoneA 中执行了 setTimeout 函数,那么在此函数调用的时

候,钩子函数 onScheduleTask 和 onHasTask 会执行,在 1秒之后,钩子函

数 onInvokeTask 会优于回调函数执行。通过这种方式,我们以更细的粒度来

操纵 setTimeout。

例子

为了更好的调试 Zone.js,我将 Zone.js 发布版的代码单独剥离了出来,发布在

zone-lab 仓库下。其中包含了相关的例子,可以通过 vscode Chrome debug

了解 Zone.js 内部的运行机制。

参考链接

Zone Primer

Angular Deep dive — Zone.js — How does it monkey patches various APIs

I reverse-engineered Zones (zone.js) and here is what I’ve found

发表评论

电子邮件地址不会被公开。 必填项已用*标注