首頁  >  文章  >  web前端  >  淺析Angular變更偵測機制,聊聊如何進行效能最佳化?

淺析Angular變更偵測機制,聊聊如何進行效能最佳化?

青灯夜游
青灯夜游轉載
2022-05-11 10:35:362523瀏覽

什麼是變更偵測?以下這篇文章帶大家了解一下Angular中的變更偵測機制,聊聊變更偵測是如何運作的,並介紹一下Angular變更偵測的效能最佳化方法,希望對大家有幫助!

淺析Angular變更偵測機制,聊聊如何進行效能最佳化?

什麼是變更偵測(Change Detection)?

變更偵測的概念

元件內的資料狀態變更以後,需要對應更新檢視。這種將視圖和資料同步的機制,就叫變化檢測。 【相關教學推薦:《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透過f​​orkZone.js並拓展出一個自己的區域NgZone,讓應用程式中的所有非同步操作都會運行在這個區域中。

Angular的變更偵測如何運作的?

Angualr會為每個元件產生一個變化監控器changeDetector ,記錄元件的變化狀態。

我們在創建了一個Angular 應用程式後,Angular 會同時建立一個 ApplicationRef  的實例,而這個實例所代表的就是我們目前所建立的這個 Angular 應用程式的實例。 ApplicationRef 創建的同時,會訂閱ngZone 中的 onMicrotaskEmpty  事件,在所有的微任務完成後調用所有的視圖的detectChanges()來執行變更檢測。

變更偵測的執行順序

  • 更新所有子子元件綁定的屬性

  • 呼叫所有子元件生命週期的鉤子OnChanges, OnInit, DoCheck,AfterContentInit

  • 更新目前元件的DOM

  • 呼叫子元件的變更偵測

  • 呼叫所有子元件的生命週期鉤子ngAfterViewInit

#舉個栗子,我們在開發模式的時候可能會遇到這種報錯:

淺析Angular變更偵測機制,聊聊如何進行效能最佳化?

這是由於變更偵測遵循從根元件開始,從上到下,執行每個元件的變更偵測,直到最後一個元件達到穩定狀態。而在下一次變更偵測之前,子孫元件都不允許去修改父元件裡的屬性。

情況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 = &#39;new Value&#39;;

變更偵測的執行策略

  • #Default 策略

    每次事件觸發變更偵測(如使用者事件、計時器、XHR、promise 等)時,此預設策略都會從上到下檢查元件樹中的每個元件。這種對元件的依賴關係不做任何假設的保守檢查方式稱為 髒檢查,這種策略在我們應用元件過多時會對我們的應用產生效能的影響。

  • #OnPush 策略

    修改元件裝飾器的changeDetection,設定為OnPush 策略後,Angular 每次觸發變更偵測後會跳過該元件和該元件的所以子元件變更偵測。

    在 OnPush 策略下,只有以下這幾種情況才會觸發元件的變化偵測:

    • 輸入值(@Input)更改(入input的值必須是一個新的引用)
    • 當前元件或子元件之一觸發了事件 (但在onPush策略中,以下操作不會觸發變更偵測)
      • setTimeout()
      • setInterval()
      • Promise.resolve().then()
      • this.http.get('...').subscribe()
    • 手動觸發變更偵測(每個元件都會關聯一個元件視圖ChangeDetectorRef)
      • #detectChanges(): 它會觸發目前元件和子元件的變更偵測
      • markForCheck(): 它不會觸發變化偵測,但會把目前的OnPush元件和所以的父元件為OnPush的元件 標記為需要偵測狀態 ,在目前或下一個變更偵測週期進行偵測
      • ApplicationRef.tick() : 它會根據元件的變更偵測策略,觸發整個應用程式的變更偵測
      淺析Angular變更偵測機制,聊聊如何進行效能最佳化?
    • async pipe

對於Angular的變更偵測如何最佳化?

由於元件預設執行 Default策略 ,任何非同步操作都會觸發整個元件數從上到下的檢查。即使Angular團隊不斷提升效能,可以在毫秒內完成上百次偵測,但當應用拓展至百上千個元件組成時,龐大的元件樹對應的變更偵測也會達到效能瓶頸。

此時,我們就需要開始分析並減少不必要的偵測次數。

如何減少偵測次數

  • #區域污染(Zone Pollution)

    #一般我們在生命週期鉤子裡使用第三方函式庫,例如chart類別庫初始化,會自帶requestAnimationRequest/setTimeout/addEventListener,我們可以將初始化方法寫入NgZonerunOutsideAngular方法中。

淺析Angular變更偵測機制,聊聊如何進行效能最佳化?

  • #OnPush 策略

    對於不涉及更新操作的視圖可以剝離出元件,使用onPush策略,以通知更新的方式刷新視圖(見上方變更偵測的執行策略 部分)。

淺析Angular變更偵測機制,聊聊如何進行效能最佳化?

  • pure pipe 取代{{function(data)}}

    在html檔案內,{{function(data)}}的寫法會導致每次變更偵測發生時,所有數值都會重新被計算。 (?:當一個1000條的列表,你只修改了其中一條數據,但另外另外999條無需更新的數據也會被重新運算。)

    此時,我們可以使用pipe的方式,只有變更的值會觸發運算,更新部分視圖。

淺析Angular變更偵測機制,聊聊如何進行效能最佳化?

  • 面對大量資料的渲染,選擇虛擬捲動/分頁請求資料

    #以上4點解決方案,來自Angular團隊的影片介紹,影片中以Angular devtool運算次數,來分析問題解決問題。所以,如果你的Angular是9 ,請繼續看吧,如何安裝Angular devtool和運行。

插件: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變更偵測機制,聊聊如何進行效能最佳化?的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:juejin.cn。如有侵權,請聯絡admin@php.cn刪除