摘要:我們再來看一下調(diào)用棧,如下圖從圖中我們發(fā)現(xiàn)了一個調(diào)用棧的代碼執(zhí)行過,還記得里提到嗎發(fā)起臟檢查的通知者,它代理了原生事件,任何一個原生異步事件的觸發(fā)都會導(dǎo)致的運行。
尋找真兇Echarts or Angular
這是一篇故事,就如同技術(shù),我們所追求的不是一個結(jié)局,而是那些深受啟發(fā)與共鳴的過程,那是我們成長的經(jīng)驗與生產(chǎn)力的積淀!
故事開始于“瘋了”的ionic3應(yīng)用頁面打開,什么也沒做5s里angular的代碼似乎一直在跑!
打開chrome性能調(diào)試工具,recorded 5秒,密密麻麻的調(diào)用棧,慘不忍睹!
Qustion1:難道真兇是angular臟檢查,發(fā)生了循環(huán)臟檢查??要弄清這個問題前,我們先來介紹angular臟檢查這個大人物。
Rope1:angular2及以上版本臟檢查方式新一代的angular一改angularjs(ng1)中受人唾棄的臟檢查策略。
數(shù)據(jù)流的改變angularjs的策略::是again and again直到穩(wěn)定。也就是說在異步事件觸發(fā)臟檢查后,臟檢查發(fā)生過程中某一個scope值改變后,會再次觸發(fā)一次臟檢查直到scope上數(shù)據(jù)穩(wěn)定不變。這樣一個過程很難找到一次臟檢查是哪一次、哪一個對象發(fā)生改變導(dǎo)致的dom更新。
angular的策略::從組件樹頂至下,各組件依次做自己的臟檢查。如下圖,左邊是model右邊是dom樹也是組件樹,每一次model數(shù)據(jù)的改變,觸發(fā)一次臟檢查,每次檢查從跟節(jié)點開始單向向下,在這次檢查時間片段中不會允許對model做修改,model數(shù)據(jù)處于穩(wěn)定狀態(tài)。
angularjs的方式: 注入ng事件來通知臟檢查,例如,你不能在js原生的setTimeout里改變model值,必須注入ng事件$setTimeout。
angularjs的方式: zone.js (它也是個big man,想了解它可以看我的一篇NgZone.js文章https://segmentfault.com/a/11...)。什么都不用做,原生隨意寫,自然有家伙幫你通知angular去做臟檢查。
answer1:從以上線索可以斷定,不是angular發(fā)生了循環(huán)臟檢查
Qustion2:是不是從組件樹頂向下逐一組件進行臟檢查,會不會是組件樹太龐大log了太多checked,執(zhí)行了太多次單個組件臟檢查?
Rope2: angular臟檢查策略先來看看現(xiàn)場,上面的圖中圈出了一段代碼changeDetection: ChangeDetectionStrategy.OnPush,它是做什么的呢?它可以改變臟檢查的策略。上面提到一顆組件樹中某一個節(jié)點某個event觸發(fā)了臟檢查,整棵組件樹每一個節(jié)點都會跟著做臟檢查對吧?對的,默認的策略是這樣的。但angular可以更聰明點,使用OnPush策略。這個策略會讓該個組件在input對象引用指針沒發(fā)生變化時跳過該節(jié)點及該節(jié)點子節(jié)點臟檢查(注意:是對象引用指針的變化)。
example 1:
@Component({ selector: "echart", template: ``, changeDetection: ChangeDetectionStrategy.OnPush }) export class ChartComponent { @Input("option") option: any; constructor() { } ngOnInit(): void { window.addEventListener("resize", this.resize, true); } click():void{ this.option= { title:"hi" } } resize() { this.option.title = "Hi" } }
這個例子中,當(dāng)頁面窗口發(fā)生變化是resize中修改title,,dom不會有任何更新。如下圖:
而當(dāng)click方法觸發(fā)時,該組件會進行臟檢查并更新dom。
如果使用了OnPush策略,又想讓resize中的修改能能更新dom怎么辦?代碼如下
constructor(private ref: ChangeDetectorRef) {} resize() { this.option.title = "Hi" this.ref.markForCheck(); }
依然是從上到下,angular會找到包含該組件的路徑的所有component進行逐一臟檢查(即使頂層組件設(shè)置了onPush策略)如下圖:
answer2:很顯然組件樹龐大不會引起臟檢查多,因為我們已經(jīng)加了onPush策略,input也未改變,該組件及該組件向下的組件都不應(yīng)該發(fā)生臟檢查
雖然加了onPush策略但頁面上依然有很多不該運行的代碼一直在執(zhí)行,下圖為頁面穩(wěn)定靜止?fàn)顟B(tài)下記錄5s內(nèi)的瀏覽器執(zhí)行情況,左圖為未加onPush策略的記錄,右圖為已加onPush策略的記錄,可以看見已加onPush策略的依然有script,render,Painting在執(zhí)行。
我們再來看一下調(diào)用棧,如下圖:
從圖中我們發(fā)現(xiàn)了一個調(diào)用棧NgZone的代碼執(zhí)行過,還記得Rope1里提到NgZone嗎?發(fā)起臟檢查的通知者,它代理了原生事件,任何一個原生異步事件的觸發(fā)都會導(dǎo)致NgZone的運行。那么一定是有原生事件在一直Loop執(zhí)行!
【注:細心的人可能還發(fā)現(xiàn)圖里有一些同學(xué)會發(fā)現(xiàn)有angular.core的代碼在執(zhí)行,不是在answer2中已經(jīng)說了不會臟檢查了嗎?確實不會在做臟檢查,rope2中也說明過臟檢查策略的原理,別忘了再臟檢查前還會check組件input引用來決定是否該組件做臟檢查呢】
Qustion3:誰在調(diào)戲NgZone?
我們再繼續(xù)看下性能分析里的調(diào)用棧,只要該函數(shù)進入過"犯罪現(xiàn)場"我們都能找到它的足跡。Look this!我們找到了一個animation.js執(zhí)行的step函數(shù)。
look this!果然有一個requestAnimationFrame定時器()原生事件一直在執(zhí)行,且從未銷毀!
answer3:原來流氓是echarts的animation.js或者說是echarts核心組件zrender在動畫結(jié)束后沒調(diào)用animation中的stop方法,總之真兇是echarts!(如果你正在使用echarts,可以打開調(diào)試工具,可以看到那段代碼一直在loop執(zhí)行)
兇手找到了,受害者還需要安撫解決,如何解決?棄用echarts?你要知道有一種流氓叫讓你討厭又讓你干不掉,不得不承認echarts的繪制效率在移動端還是不錯的,還有地圖,用其它chart plugin誰來給你畫某某市地圖...
此時不得不再捧一把Angular,雖然我們管不了echarts,但NgZone是一個很開放的家伙。給我們很多自由操作的空間,就像下面的sample,使用runOutsideAngular將包裹的函數(shù)內(nèi)部執(zhí)行的代碼都跳過zone.js的包裝。那個echarts的requestAnimationFrame再也不會騷擾咱們的NgZone了。
export class EChartsComponent implements OnInit, OnDestroy { @Input() chartid: string; @Input("option") option: any; private chart: any; @ViewChild("root") private root; constructor(private ngZone: NgZone) { } resizeListener = () => this.resize(); ngOnInit(): void { this.ngZone.runOutsideAngular(() => { this.chart = echarts.init(this.root.nativeElement); this.chart.setOption(this.option, true); window.addEventListener("resize", this.resizeListener, true); }) } }
優(yōu)化后5s內(nèi)perfermance如圖:
故事的結(jié)局雖然優(yōu)化的結(jié)果不是最完美的,從圖上可以看到頁面穩(wěn)定靜止?fàn)顟B(tài)下還是有script(echarts的bad code)在執(zhí)行。如何去解決echarts的loop requestAnimationFrame問題,后續(xù)提issue留給echarts團隊去解決吧。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/89909.html
摘要:最近一段時間做了一個使用的圖表項目。由于理解能力有限,使用起來并非暢通無阻。所謂好記性不如爛筆頭,現(xiàn)將一些比較關(guān)鍵的點記錄一下,供后續(xù)查看。 最近一段時間做了一個使用echarts的圖表項目。由于理解API能力有限,使用起來并非暢通無阻。所謂好記性不如爛筆頭,現(xiàn)將一些比較關(guān)鍵的點記錄一下,供后續(xù)查看。 一 使用方法 項目:ionic+angular4+echarts 1.由于打包原因,...
摘要:開發(fā)我認為在中使用其他插件的最好方法是使用指令的形式在項目中引入,這樣做有許多好處,其中最明顯的好處便是當(dāng)項目中需要引入多種插件時可以使用各種不同的指令將他們分離并且還具有一次開發(fā)各處使用的組件化特點。 在實習(xí)期間,由于項目的需求,我第一次系統(tǒng)的使用了angular這一優(yōu)秀的js框架,其所擁有的許多優(yōu)秀特性極大的方便了項目的開發(fā),然而在開發(fā)中也遇到過不少的問題,現(xiàn)在趁自己被抓回學(xué)校無所...
摘要:安裝在項目中引入文件使用在你真正需要使用指令的中堆疊區(qū)域圖郵件營銷聯(lián)盟廣告視頻廣告直接訪問搜索引擎周一周二周三周四周五周六周日郵件營銷總量聯(lián)盟廣告總量視頻廣告總量直接訪問總量搜索引擎總量直達營銷廣告搜索引擎郵件營銷聯(lián) 1.安裝ngx-echarts //if you use npmnpm install echarts --save //if your angular version ...
閱讀 2121·2023-04-26 02:19
閱讀 1914·2021-11-19 09:40
閱讀 1704·2021-09-29 09:35
閱讀 3575·2021-09-29 09:34
閱讀 4297·2021-09-07 10:16
閱讀 5530·2021-08-11 11:14
閱讀 3578·2019-08-30 15:54
閱讀 1629·2019-08-30 15:53