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