摘要:并不是真正的進(jìn)入,而是通過(guò)包裹的方式偽造執(zhí)行上下文,并通過(guò)鉤子函數(shù)方便的進(jìn)入執(zhí)行環(huán)境。如何使用運(yùn)行結(jié)果可以從上面的看到運(yùn)用提供的,鉤子函數(shù)方便的進(jìn)入了執(zhí)行的上下文,記錄了時(shí)間。我們還有個(gè)需求,需要因人而異的處理這些暴露的鉤子函數(shù)。
angular2 臟檢查總述
這系列文章將介紹angular2的臟值檢查是如何工作的?如何比ng1更高效?帶著上述問(wèn)題,讓我們一起來(lái)看看angular2這禽獸(誰(shuí)讓它叫angular,又那么生猛)干了什么。
什么是臟值檢查片面的說(shuō)臟檢查是對(duì)比當(dāng)前的數(shù)據(jù)和曾經(jīng)的數(shù)據(jù)是否發(fā)生改變。而在這個(gè)context下,我想介紹的是angular2從發(fā)現(xiàn)數(shù)據(jù)的變化到找到變化的點(diǎn)到更新DOM的整個(gè)過(guò)程。也就是說(shuō)這里所說(shuō)的臟值檢查是Viewmodel與view層的那座橋梁。先看下面的圖,紅色表示改變的節(jié)點(diǎn)。
那么問(wèn)題來(lái)了,angular2是如何知道數(shù)據(jù)發(fā)生了改變?又是如何知道需要修改DOM的位置,準(zhǔn)確的最小范圍的修改DOM呢?沒(méi)錯(cuò),盡可能小的范圍修改DOM,因?yàn)椴僮鱀OM對(duì)于性能來(lái)說(shuō)可是一件奢侈品。別急,讓我們先看看沒(méi)有angular我們?nèi)绾螌?shí)現(xiàn)數(shù)據(jù)改變到view的改變。在web古老的年代,那個(gè)asp.net、j2ee、php的時(shí)代,請(qǐng)求+整頁(yè)重繪,那時(shí)啪啪啪的重繪聲,如今依然回蕩在心中,痛苦不可磨滅。再來(lái)看看SPA時(shí)代,其它framework的解決方案,最值得一提的是名聲在外的react用了diff虛擬DOM的方式,也實(shí)現(xiàn)了最小化更新DOM。有興趣可以看看Tero的這篇博客,比較了很多流行框架對(duì)這個(gè)問(wèn)題的解決。
http://teropa.info/blog/2015/...
回到上面問(wèn)題,angular2是如何知道數(shù)據(jù)發(fā)生了改變?細(xì)心的你可能會(huì)發(fā)現(xiàn),在angular2 示例項(xiàng)目中都引入了一個(gè)Zone.js的東西。Zone.js是什么鬼?
Zone提供方便的方式”進(jìn)入”異步函數(shù)執(zhí)行上下文(注意進(jìn)入有引號(hào),后面解釋),并能在異步執(zhí)行環(huán)境中加入一些鉤子的東西。
為什么需要進(jìn)入異步函數(shù)的執(zhí)行上下文?這是我看到zone.js的github的第一個(gè)問(wèn)題。我們先來(lái)看看這樣一個(gè)場(chǎng)景。
foo(); setTimeout(doSomething, 2000); bar(); baz();
我任性的提出一個(gè)問(wèn)題,我想知道上面doSomething函數(shù)在這個(gè)上下文中什么時(shí)候開(kāi)始執(zhí)行的?要知道為了不阻塞UI界面的用戶體驗(yàn),在JavaScript執(zhí)行的很多耗時(shí)操作都被封裝為了異步操作,如:setTimeout、XMLHttpRequest、DOM事件等。也就是說(shuō)doSomething會(huì)進(jìn)入事件循環(huán)。 這個(gè)時(shí)候是不是特別期望,能進(jìn)入doSomething的執(zhí)行環(huán)境,拿到點(diǎn)證據(jù)控告寫doSomething這個(gè)函數(shù)的程序員寫得垃圾?可能你已經(jīng)想到了解決辦法,雖然doSomething的執(zhí)行上下文我進(jìn)不了。但我可以wrap一下doSomething偽造一個(gè)執(zhí)行上下文,在這個(gè)上下文中做點(diǎn)手腳,哼哼.. 恭喜你,你已經(jīng)有了和Zone.js團(tuán)隊(duì)成員一樣的思想覺(jué)悟。
這也是為什么上面提到的Zone提供方便的方式”進(jìn)入”異步函數(shù)執(zhí)行上下文中進(jìn)入加了引號(hào)。并不是真正的進(jìn)入,而是通過(guò)包裹的方式偽造執(zhí)行上下文,并通過(guò)鉤子函數(shù)方便的進(jìn)入執(zhí)行環(huán)境。這個(gè)場(chǎng)景看似有些極端,但在異步Task跟蹤,分析,錯(cuò)誤記錄、開(kāi)發(fā)調(diào)試跟蹤等場(chǎng)景,都有這樣的需求。下面我們來(lái)看看Zone是如何提供方便的。
demo1
var profilingZone = (function () { var time = 0, timer = performance ? performance.now.bind(performance) : Date.now.bind(Date); return { beforeTask: function () { this.start = timer(); console.log(‘beforeTask time:’+this.start); }, afterTask: function () { time += timer() - this.start; console.log(‘a(chǎn)fterTask time:’+time); } }; }()); zone.fork(profilingZone).run(function(){ foo(); setTimeout(doSomething, 2000); bar(); baz(); });
demo1運(yùn)行結(jié)果
// beforeTask time:3073872.9000000004 // AfterTask time:1.04500000039116 // beforeTask time:3075873.165 // AfterTask time:1.2550000004
可以從上面的demo看到運(yùn)用Zone提供的beforeTask,afterTask鉤子函數(shù)方便的進(jìn)入了doSomething執(zhí)行的上下文,記錄了時(shí)間。值得一提的是,我們并沒(méi)有對(duì)doSomething做任何處理,我們所做的只是在doSomething外部做了點(diǎn)改動(dòng)。就達(dá)到了進(jìn)入doSomething執(zhí)行上下文的目的。似乎看到了AOP的思想(說(shuō)到AOP我又想到了ng2的annotation,找個(gè)時(shí)間好好分享一下)。 除此之外Zone還提供了一些其它鉤子函數(shù)。請(qǐng)參考:https://github.com/angular/zo...
Zone原理yo! check it out! demo的運(yùn)行結(jié)果為什么會(huì)有輸出兩次beforeTask和AfterTask?要想解答這個(gè)問(wèn)題,我們先來(lái)看看Zone運(yùn)行的原理。前面提到過(guò)Zone偽造一個(gè)執(zhí)行上下文,實(shí)際上Zone有一個(gè)叫猴子補(bǔ)丁的東西。在Zone.js運(yùn)行時(shí),就會(huì)為這些異步事件做一層代理包裹,也就是說(shuō)Zone.js運(yùn)行后,調(diào)用setTimeout、addEventListener等瀏覽器異步事件時(shí),不再是調(diào)用原生的方法,而是被猴子補(bǔ)丁包裝過(guò)后的代理方法。wo!猴子補(bǔ)丁真牛逼,它是怎么把這些原生的事件都進(jìn)行包裝改造后進(jìn)化成“猴子”的呢?其實(shí)很簡(jiǎn)單,其實(shí)并不難..只需要暴力點(diǎn)!再暴力點(diǎn)!
//以下是Zone.js啟動(dòng)時(shí)執(zhí)行邏輯的抽象代碼片段 function zoneAwareAddEventListener() {...} function zoneAwareRemoveEventListener() {...} function zoneAwarePromise() {...} function patchTimeout() {...} window.prototype.addEventListener = zoneAwareAddEventListener; window.prototype.removeEventListener = zoneAwareRemoveEventListener; window.prototype.promise = zoneAwarePromise; window.prototype.setTimeout = patchTimeout;
確實(shí)很暴力,直接原生覆蓋了!原生的異步方法都被代理覆蓋了,代理里setup了鉤子函數(shù),這還不能完全解決問(wèn)題。我們還有個(gè)需求,需要“因人而異”的處理這些暴露的鉤子函數(shù)。例如
setTimeout(doA, 2000); setTimeout(doB, 2000);
這里有兩個(gè)方法doA和doB,總不能用鉤子函數(shù)里只能做同樣的事情吧。所以會(huì)有一個(gè)根zone和fork。fork可以擴(kuò)展一個(gè)新的zone。而每個(gè)zone都有自己的生命周期。為了理解這個(gè)問(wèn)題我們?cè)賮?lái)看個(gè)Demo
demo2
//fork一個(gè)新的zone,我們給它暫定個(gè)名字叫temporary zone. Zone.current.fork({}).run(function () { //調(diào)用beforeTask等鉤子(zone內(nèi)部處理) //run 內(nèi)部Zone.current指向temporary zone(zone內(nèi)部做的處理),并添加一個(gè)inTheZone屬性設(shè)置為true. Zone.current.inTheZone = true; //調(diào)用被猴子補(bǔ)丁包裝后的setTimeout方法,并將包裝后的greet方法內(nèi)部的zone設(shè)置成當(dāng)前的temporary zone,并將函數(shù)greet加入事件循環(huán). setTimeout(function greet() { console.log("in the zone: " + !!Zone.current.inTheZone); }, 0); //要在zone run中執(zhí)行的內(nèi)容已經(jīng)執(zhí)行完了,調(diào)用AfterTask鉤子.(zone內(nèi)部處理) // //調(diào)用afterTask等鉤子.(zone內(nèi)部處理) //zone.current引用替換成根zone,因?yàn)閞un外部的zone不應(yīng)該是fork后的zone,fork后的zone生命周期隨著run的結(jié)束而結(jié)束.(zone內(nèi)部處理) }); console.log("in the zone: " + !!Zone.current.inTheZone);
demo2輸出結(jié)果
in the zone: false in the zone: true
希望更好的理解,我在demo中加了注釋以說(shuō)明zone生命周期的問(wèn)題.我們可以看到fork后的temporary zone生命周期隨著run執(zhí)行的結(jié)束而結(jié)束.所以run外部的console.log取不到Zone.current里的屬性inTheZone(temporary zone中的inTheZone)而在greet真正執(zhí)行時(shí),也會(huì)經(jīng)歷和run內(nèi)部一樣的過(guò)程(鉤子函數(shù)的執(zhí)行,zone的引用替換銷毀等).而包裹后的greet內(nèi)部的zone指向的是在setTimeout傳入greet上下文中的(當(dāng)前作用域中)temporary zone.
現(xiàn)在再回頭看看demo1中為什么會(huì)輸出兩次beforeTask和AfterTask,也正是因?yàn)閦one特定的生命周期所造成的.
還記得大明湖畔ng1的$scope.$apply嗎?任何原生的事件都不會(huì)觸發(fā)臟檢查,必須得調(diào)用$scope.$apply來(lái)告訴angular。我的數(shù)據(jù)有更新了,你同步更新下UI吧。而在angular2中有了Zone.js。原生隨便用,setTimeout,addEventListener、promise等都在ngZone中執(zhí)行,angular并在ngZone中setup了相應(yīng)的鉤子,通知angular2做相應(yīng)的臟檢查處理,然后更新DOM。
如何臟檢查?如何更新DOM?比起angular1有什么新的變化?下章再見(jiàn)。希望上述內(nèi)容能給你一些幫助。如有任何疑問(wèn)與不足,歡迎指出并討論。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/80322.html
摘要:策略減少檢測(cè)次數(shù)當(dāng)輸入屬性不變時(shí),可以跳過(guò)整個(gè)變更檢測(cè)子樹(shù)?,F(xiàn)在當(dāng)執(zhí)行更改檢測(cè)時(shí),它將從上到下進(jìn)行。并且一旦更改檢測(cè)運(yùn)行結(jié)束,它將恢復(fù)整個(gè)樹(shù)的狀態(tài)。 Angular 2.x+ 臟檢查機(jī)制理解 目前幾種主流的前端框架都已經(jīng)實(shí)現(xiàn)雙向綁定特性,但實(shí)現(xiàn)的方法各有不同: 發(fā)布者-訂閱者模式(backbone.js) 臟值檢查(angular.js) 數(shù)據(jù)劫持 + 發(fā)布者-訂閱者模式(vue.j...
摘要:我們?cè)賮?lái)看一下調(diào)用棧,如下圖從圖中我們發(fā)現(xiàn)了一個(gè)調(diào)用棧的代碼執(zhí)行過(guò),還記得里提到嗎發(fā)起臟檢查的通知者,它代理了原生事件,任何一個(gè)原生異步事件的觸發(fā)都會(huì)導(dǎo)致的運(yùn)行。 尋找真兇Echarts or Angular 這是一篇故事,就如同技術(shù),我們所追求的不是一個(gè)結(jié)局,而是那些深受啟發(fā)與共鳴的過(guò)程,那是我們成長(zhǎng)的經(jīng)驗(yàn)與生產(chǎn)力的積淀! 故事開(kāi)始于瘋了的ionic3應(yīng)用 頁(yè)面打開(kāi),什么也沒(méi)做5s里...
angular2是什么?我猜不容我贅述,各位一定略有耳聞,無(wú)論是曾經(jīng)AngularJS的擁躉,亦或是React的粉絲,都或多或少的對(duì)她有過(guò)一點(diǎn)了解。未見(jiàn)其物、先聞其聲,angular2在問(wèn)世之前已經(jīng)做足了宣傳,想必諸位也一定被下面各種詞匯所震懾,什么:TypeScript、 ES5、 ES6、 Dart、 Immutable、 Unidirectional Data Flow、 Reactive ...
閱讀 1290·2021-11-11 16:54
閱讀 1843·2021-10-13 09:40
閱讀 1015·2021-10-08 10:05
閱讀 3572·2021-09-22 15:50
閱讀 3799·2021-09-22 15:41
閱讀 2023·2021-09-22 15:08
閱讀 2422·2021-09-07 10:24
閱讀 3636·2019-08-30 12:52