您好,登錄后才能下訂單哦!
本篇內容介紹了“Angular變更檢測機制怎么進行性能優化”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
組件內的數據狀態變化以后,需要對應更新視圖。這種將視圖和數據同步的機制,就叫變化檢測。
只要發生了異步操作(Events, Timer, XHR),Angular 就會認為有狀態可能發生變化了,然后就會進行變更檢測。
Events::click,mouseover,mouseout,keyup,keydown 等瀏覽器事件;
Timer:setTimeout/setInterval;
XHR:各類請求等。
既然都是對異步操作進行變更檢測,那么Angular是如何訂閱異步請求,進行變更檢測的呢?
這里介紹下NgZone
以及它的fork對象Zone.js
。
Zone.js
用于封裝和攔截瀏覽器中的異步活動、它還提供 異步生命周期的鉤子 和 統一的異步錯誤處理機制。
Zone.js
是通過 Monkey Patching(猴子補丁) 的方式來對瀏覽器中的常見方法和元素進行攔截,例如 setTimeout
和 HTMLElement.prototype.onclick
。Angular 在啟動時會利用 Zone.js
修補幾個低級瀏覽器 API,從而實現異步事件的捕獲,并在捕獲時間后調用變更檢測。
Angular通過forkZone.js
并拓展出一個自己的區域NgZone
,讓應用中的所有異步操作都會運行在這個區域中。
Angualr會為每一個組件生成一個變化監測器changeDetector
,記錄組件的變化狀態。
我們在創建了一個Angular 應用后,Angular 會同時創建一個 ApplicationRef
的實例,這個實例代表的就是我們當前創建的這個 Angular 應用的實例。 ApplicationRef
創建的同時,會訂閱 ngZone 中的 onMicrotaskEmpty 事件,在所有的微任務完成后調用所有的視圖的detectChanges()
來執行變更檢測。
更新所有子子組件綁定的屬性
調用所有子組件生命周期的鉤子 OnChanges, OnInit, DoCheck,AfterContentInit
更新當前組件的DOM
調用子組件的變更檢測
調用所有子組件的生命周期鉤子 ngAfterViewInit
舉個栗子,我們在開發模式的時候可能會遇到這種報錯:
這是由于變更檢測遵循從根組件開始,從上到下,執行每個組件的變更檢測,直到最后一個組件達到穩定狀態。而在下一次變更檢測之前,子孫組件都不允許去修改父組件里的屬性。
情況1 在開發模式下,Angular會進行二次檢測 (生產環境下調用enableProdMode()
,檢測次數會降為1)。一旦我們在 第4步 完成后,在子孫組件里修改父組件的屬性,那么,Angular在執行第二次檢測的時候,發現兩次的值不一致,就會出現上述錯誤。
情況2 只要父組件對子組件做了屬性綁定,那么不管是在OnChanges,OnInit,DoCheck,AfterContentInit 和 AfterViewInit 中的任意一個生命周期鉤子中執行下述代碼,也會報錯。
// #parent {{data}} <child [data]="data"></child> // in child component ts, execute: this.parent.data = 'new Value';
Default 策略
每次事件觸發變化檢測(如用戶事件、計時器、XHR、promise 等)時,此默認策略都會從上到下檢查組件樹中的每個組件。這種對組件的依賴關系不做任何假設的保守檢查方式稱為 臟檢查,這種策略在我們應用組件過多時會對我們的應用產生性能的影響。
OnPush 策略
修改組件裝飾器的changeDetection
,設置為 OnPush 策略后,Angular 每次觸發變化檢測后會跳過該組件和該組件的所以子組件變化檢測。
在 OnPush
策略下,只有以下這幾種情況才會觸發組件的變化檢測:
detectChanges(): 它會觸發當前組件和子組件的變化檢測
markForCheck(): 它不會觸發變化檢測,但是會把當前的OnPush組件和所以的父組件為OnPush的組件 標記為需要檢測狀態 ,在當前或者下一個變化檢測周期進行檢測
ApplicationRef.tick() : 它會根據組件的變化檢測策略,觸發整個應用程序的更改檢測
setTimeout()
setInterval()
Promise.resolve().then()
this.http.get('...').subscribe()
輸入值(@Input)更改(入input的值必須是一個新的引用)
當前組件或子組件之一觸發了事件 (但在onPush策略中,以下操作不會觸發變更檢測)
手動觸發變更檢測(每個組件都會關聯一個組件視圖ChangeDetectorRef)
async pipe
由于組件默認執行 Default策略 ,任何異步操作都會觸發整個組件數從上到下的檢查。即使Angular團隊不斷提升性能,可以在毫秒內完成上百次檢測,但是當應用拓展至百上千個組件組成時,龐大的組件樹對應的變更檢測也會達到性能瓶頸。
此時,我們就需要開始分析并減少不必要的檢測次數。
區域污染(Zone Pollution)
一般我們在生命周期鉤子里使用第三方庫,比如chart類庫初始化,會自帶requestAnimationRequest/setTimeout/addEventListener,我們可以將初始化方法寫入NgZone
的runOutsideAngular
方法中。
OnPush 策略
對于不涉及更新操作的視圖可以剝離出組件,使用onPush策略,以通知更新的方式刷新視圖(見上方 變更檢測的執行策略 部分)。
用 pure pipe 代替 {{function(data)}}
在html文件內,{{function(data)}}
的寫法會導致每次變更檢測發生時,所有數值都會重新被計算。(?:當一個1000條的列表,你只修改了其中一條數據,但另外另外999條無需更新的數據也會被重新運算。)
此時,我們可以使用pipe的方式,只有變更的值會觸發運算,更新部分視圖。
面對大量數據的渲染,選擇虛擬滾動/分頁請求數據
以上4點解決方案,來源于Angular團隊的視頻介紹,視頻中以Angular devtool運算次數,來分析問題解決問題。所以,如果你的Angular是9+,請繼續看下去吧,如何安裝Angular devtool和運行。
Angular 9+, 支持Ivy。
Guide和下載地址
保證運行環境為開發環境
// environment.dev.ts ... production: false ...
angular.json > dev配置項 > "optimization": false
projects > your-project-name > architect > build > configurations > dev > "optimization": false
“Angular變更檢測機制怎么進行性能優化”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。