共计 3623 个字符,预计需要花费 10 分钟才能阅读完成。
或者你据说过 Angular 应用了zone.js
, 但 Angular 为什么要应用zone.js
, 它可能提供哪些性能呢?明天咱们独自写一篇文章聊聊zone.js
,对于它在 Angular 框架中施展的作用将在下一篇文章讲述。
什么是 Zone ? 官网文档是这么解释的:Zone 是一个跨多个异步工作的执行上下文。一句话总结来说,Zone 在拦挡或追踪异步工作方面有着特地弱小的能力。上面咱们将通过一个示例来展现它的能力,并简略分析一下背地的工作原理。
<button id="b1">Bind Error</button>
<button id="b2">Cause Error</button>
<script>
function main() {b1.addEventListener('click', bindSecondButton);
}
function bindSecondButton() {b2.addEventListener('click', throwError);
}
function throwError() {throw new Error('aw shucks');
}
main();
</script>
这是一个简略的 HTML 页面。页面加载时会给第一个按钮增加点击事件,其点击事件函数的性能是给第二个按钮增加点击事件,而第二个按钮的点击事件函数性能是抛出一个异样。咱们顺次点击第一个按钮和第二个按钮,控制台显示如下:
(索引):26 Uncaught Error: aw shucks
at HTMLButtonElement.throwError ((索引):26:13)
然而如果咱们通过 zone.js
启动运行代码,控制台输入会有什么不同呢,咱们先调整启动代码:
Zone.current.fork(
{
name: 'error',
onHandleError: function (parentZoneDelegate, currentZone, targetZone, error) {console.log(error.stack);
}
}
).fork(Zone.longStackTraceZoneSpec).run(main);
此时控制台输入如下:
Error: aw shucks
at HTMLButtonElement.throwError ((索引):26:13)
at ZoneDelegate.invokeTask (zone.js:406:31)
at Zone.runTask (zone.js:178:47)
at ZoneTask.invokeTask [as invoke] (zone.js:487:34)
at invokeTask (zone.js:1600:14)
at HTMLButtonElement.globalZoneAwareCallback (zone.js:1626:17)
at ____________________Elapsed_571_ms__At__Mon_Jan_31_2022_20_09_09_GMT_0800_________ (localhost)
at Object.onScheduleTask (long-stack-trace-zone.js:105:22)
at ZoneDelegate.scheduleTask (zone.js:386:51)
at Zone.scheduleTask (zone.js:221:43)
at Zone.scheduleEventTask (zone.js:247:25)
at HTMLButtonElement.addEventListener (zone.js:1907:35)
at HTMLButtonElement.bindSecondButton ((索引):23:10)
at ZoneDelegate.invokeTask (zone.js:406:31)
at Zone.runTask (zone.js:178:47)
at ____________________Elapsed_2508_ms__At__Mon_Jan_31_2022_20_09_06_GMT_0800_________ (localhost)
at Object.onScheduleTask (long-stack-trace-zone.js:105:22)
at ZoneDelegate.scheduleTask (zone.js:386:51)
at Zone.scheduleTask (zone.js:221:43)
at Zone.scheduleEventTask (zone.js:247:25)
at HTMLButtonElement.addEventListener (zone.js:1907:35)
at main ((索引):20:10)
at ZoneDelegate.invoke (zone.js:372:26)
at Zone.run (zone.js:134:43)
通过比照咱们晓得:不引入 zone.js
时,咱们通过谬误调用栈仅仅可能晓得,异样是由按钮 2 的点击函数抛出。而引入了 zone.js
后,咱们不仅晓得异样是由按钮 2 的点击函数抛出,还晓得它的点击函数是由按钮 1 的点击函数绑定的,甚至可能晓得最开始的利用启动是 main
函数触发。这种可能继续追踪多个异步工作的能力在大型简单我的项目中异样重要,当初咱们来看 zone.js
是如何做到的吧。
zone.js
接管了浏览器提供的异步 API,比方点击事件、计时器等等。也正是因为这样,它才可能对异步操作有更强的管制染指能力,提供更多的能力。当初咱们拿点击事件举例,看看它是如何做到的吧。
proto[ADD_EVENT_LISTENER] = makeAddListener(nativeAddEventListener,..)
上述代码中,proto
便指的是 EventTarget.prototype
,也就是说这行代码从新定义了addEventListener
函数。咱们持续看看 makeAddListener
函数做了什么。
function makeAddListener() {
......
// 要害代码 1
nativeListener.apply(this, arguments);
......
// 要害代码 2
const task = zone.scheduleEventTask(source, ...)
......
}
该函数次要做了两件事,一是在自定义函数中执行浏览器自身提供的 addEventListener
函数,另外一个就是为每个点击函数安顿了一个事件工作,这也是 zone.js
对异步 API 有弱小染指能力的重要因素。
当初咱们再回到本文结尾的示例中,看看控制台为什么可能输入残缺的残缺的函数调用栈。刚刚咱们剖析过了 makeAddListener
函数,其中提到它为每个点击函数安顿了一个事件工作,也就是 zone.scheduleEventTask
函数的执行。这个安顿事件工作函数最终其实执行的是onScheduleTask
:
onScheduleTask: function (..., task) {
const currentTask = Zone.currentTask;
let trace = currentTask && currentTask.data && currentTask.data[creationTrace] || [];
trace = [new LongStackTrace()].concat(trace);
task.data[creationTrace] = trace;
}
文章结尾控制台输入的残缺的函数调用栈,存储在 currentTask.data[creationTrace]
外面,它是一个由 LongStackTrace
实例组成的数组。每次有异步工作产生时,onScheduleTask
函数便把以后函数调用栈存储记录下来,咱们看看类 LongStackTrace
的结构器就晓得了:
class LongStackTrace {constructor() {this.error = getStacktrace();
this.timestamp = new Date();}
}
function getStacktraceWithUncaughtError() {return new Error(ERROR_TAG);
}
this.error
存储的便是函数调用栈,getStacktrace
函数通常调用的是 getStacktraceWithUncaughtError
函数,咱们看到 new Error
大略就可能晓得整个调用栈是如何得来的了。
本文剖析的只是 zone.js
能力的一个示例,如果你心愿理解更多功能能够参阅官网文档。通过这个示例,心愿读者能对 zone.js
有一个大略的意识,因为它也是 Angular 变更检测不可或缺的基石。这方面的内容我将在下一篇文章中解说。欢送关注我的集体微信公众号【朱玉洁的博客】,后续将带来更多前端常识分享。