日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Angular变化检测机制:改善的脏检查

發(fā)布時間:2023/12/31 编程问答 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Angular变化检测机制:改善的脏检查 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

本文鏈接:https://blog.csdn.net/fen747042796/article/details/75152336

前端展示的頁面是由視圖和數據共同構成的,視圖模板定義了頁面的框架,而數據定義了頁面具體的顯示內容。

而數據發(fā)生變化的時候,我們需要及時將變化的內容更新到視圖中,否則用戶看到的數據就是不正確的。系統(tǒng)及時感知到數據模型的變化,然后通過計算更新到視圖中,這是每個前端框架都需要解決的問題。這前一半部分就是所謂的變化檢測。

數據何時變化

接下來的問題是,數據何時變化,哪些因素會引起數據變化?在數據雙向綁定的分析和簡單實現(xiàn)中曾經分析過,主要有如下幾種情況可能也改變數據:

  • 用戶輸入操作,比如點擊,提交等
  • 請求服務端數據
  • 定時事件,比如setTimeout,setInterval

這幾點有一個共同點,就是它們都是異步的。也就是說,所有的異步操作是可能導致數據變化的根源因素。

如何通知變化

那么,在Angular中是誰來通知數據即將變化的呢?在AngularJS中是由代碼$scope.$apply()或者$scope.$digest觸發(fā),而Angular接入了ZoneJS,由它監(jiān)聽了Angular所有的異步事件。ZoneJS是怎么做到的呢?其實它重寫了所有的異步api(所謂的猴子補丁Monkey patch)!ZoneJS會通知Angular可能有數據發(fā)生變化,需要檢測更新。

變化檢測原理

Angular得到需要重新檢查數據模型,更新視圖的通知后,是怎么執(zhí)行變化檢測的呢?答案是,臟檢查。考慮到還沒聽說過臟檢查的同學,這里解釋一下,臟檢查其實就是存儲所有變量的值,每當可能有變量發(fā)生變化需要檢查時,就將所有變量的舊值跟新值進行比較,不相等就說明檢測到變化,需要更新對應視圖。

改善的臟檢查

接觸過AngularJS的同學肯定知道,它使用的變化檢測機制也是臟檢查。那么,同是臟檢查的背后,有何不同呢?為何Angular自稱變化檢測的性能比起AngularJS提升了很多?

Angular的核心是組件化,組件的嵌套會使得最終形成一棵組件樹。Angular的變化檢測可以分組件進行,每個組件都有對應的變化檢測器ChangeDetector??上攵?#xff0c;這些變化檢測器也會構成一棵樹。

另外,Angular的數據流是自頂而下,從父組件到子組件單向流動。單向數據流向保證了高效、可預測的變化檢測。盡管檢查了父組件之后,子組件可能會改變父組件的數據使得父組件需要再次被檢查,這是不被推薦的數據處理方式。在開發(fā)模式下,Angular會進行二次檢查,如果出現(xiàn)上述情況,二次檢查就會報錯:ExpressionChangedAfterItHasBeenCheckedError(關于這個問題的答案,可以在參考資料中找到)。而在生產環(huán)境中,臟檢查只會執(zhí)行一次。

相比之下,AngularJS采用的是雙向數據流,錯綜復雜的數據流使得它不得不多次檢查,使得數據最終趨向穩(wěn)定。理論上,數據可能永遠不穩(wěn)定。AngularJS給出的策略是,臟檢查超過10次,就認為程序有問題,不再進行檢查。這個10,我不知道它的給出依據是什么,也許是個經驗值吧。

變化檢測策略onPush

Angular還讓開發(fā)者擁有定制變化檢測策略的能力。

  • export enum ChangeDetectionStrategy {
  • OnPush, // 表示變化檢測對象的狀態(tài)為`CheckOnce`
  • Default, // 表示變化檢測對象的狀態(tài)為`CheckAlways`
  • }
  • 從ChangeDetectionStrategy可以看到,Angular有兩種變化檢測策略。Default是Angular默認的變化檢測策略,也就是上述提到的臟檢查(只要有值發(fā)生變化,就全部檢查)。開發(fā)者可以根據場景來設置更加高效的變化檢測方式:onPush。onPush策略,就是只有當輸入數據的引用發(fā)生變化或者有事件觸發(fā)時,組件才進行變化檢測。

  • @Component({
  • template: `
  • <h2>{{vData.name}}</h2>
  • <span>{{vData.email}}</span>
  • `,
  • // 設置該組件的變化檢測策略為onPush
  • changeDetection: ChangeDetectionStrategy.OnPush
  • })
  • class VCardCmp {
  • @Input() vData;
  • }
  • 比如上面這個例子,當vData的屬性值發(fā)生變化的時候,這個組件不會發(fā)生變化檢測,只有當vData重新賦值的時候才會。一般,只接受輸入的木偶子組件(dumb components)比較適合采用onPush策略。

    那什么時候只要對象的屬性值發(fā)生變化,整個對象的引用就變了呢?不可變對象(Immutable Object)。當組件中的輸入對象是不變量時,可采用onPush變化檢測策略,減少變化檢測的頻率。換個角度來說,為了更加智能地執(zhí)行變化檢測,可以在只接受輸入的子組件中采用onPush策略。

    變化檢測對象引用

    Angular不僅可以讓開發(fā)者設置變化檢測的策略,還可以讓開發(fā)者獲取變化檢測對象引用ChangeDetectorRef,手動去操作變化檢測。變化檢測對象引用給開發(fā)者提供的方法有以下幾種:

    • markForCheck():將檢查組件的所有父組件所有子組件,即使設置了變化檢測策略為onPush
    • detach():將變化檢測對象脫離檢測對象樹,不再進行變化檢查;結合detectChanges可實現(xiàn)局部變化檢測
    • detectChanges():將檢測該組件及其子組件,結合detach可實現(xiàn)局部檢測
    • checkNoChanges(): 檢測該組件及其子組件,如果有變化存在則報錯,用于開發(fā)階段二次驗證變化已經完成
    • reattach():將脫離的變化檢測對象重新鏈接到變化檢測樹上

    那么,如果是Observable的話,它會訂閱所有的變量變化,只要在訂閱回調函數中手動觸發(fā)變化檢測即可實現(xiàn)最小成本的檢測(仍采用onPush變化檢測策略)。舉個例子:

  • @Component({
  • template: '{{counter}}',
  • changeDetection: ChangeDetectionStrategy.OnPush
  • })
  • class CartBadgeCmp {
  • @Input() addItemStream:Observable<any>;
  • counter = 0;
  • constructor(private cd: ChangeDetectorRef) {}
  • ngOnInit() {
  • this.addItemStream.subscribe(() => {
  • this.counter++; // 數據模型發(fā)生變化
  • this.cd.markForCheck(); // 手動觸發(fā)檢測
  • })
  • }
  • }
  • 另外,當數據模型變化太過頻繁,我們可自定義變化檢測的時機。舉個例子:

  • @Component({
  • template: `{{counter}}
  • <input type="check" (click)="toggle()">`,
  • })
  • class CartBadgeCmp {
  • counter = 0;
  • detectEnabled = false;
  • constructor(private cd: ChangeDetectorRef) {}
  • ngOnInit() {
  • //10毫秒增加1
  • setInterval(()=>{this.counter++}, 10);
  • }
  • toggle(){
  • if( this.detectEnabled ){
  • this.cd.reattach(); // 鏈接上變化檢測樹
  • }
  • else{
  • this.cd.detach(); // 脫離變化檢測樹
  • }
  • }
  • }
  • 一個注意點是,采用onPush策略之后的組件detach()無效,具體可參考這里。

    疑惑點

    在Angular源碼中看到變化檢測對象有如下幾種狀態(tài):?
    +?CheckOnce:表示只檢查一次,調用detectChanges之后狀態(tài)將會變?yōu)镃hecked?
    +?Checked:表示在狀態(tài)變?yōu)镃heckOnce之前會跳過所有檢測?
    +?CheckAlways:表示總是接受變化檢測,每次調用detectChanges后狀態(tài)還是CheckAlways?
    +?Detached:代表變化檢測對象脫離了變化檢測對象樹,不再進行變化檢測?
    +?Errored:表述變化測試對象發(fā)生錯誤,變化檢測實效?
    +?Destroyed:表示變化檢測對象已經被銷毀

    而OnPush策略表示變化檢測對象的狀態(tài)為CheckOnce。那么設置OnPush策略的組件為什么是引用發(fā)生變化之后才會執(zhí)行變化檢測的?檢測之后狀態(tài)從CheckOnce變成Checked,然后是如何變成CheckOnce的?

    P.S. 嘗試閱讀Angular變化檢測這部分的源代碼,實在不知從何下手,網上資料甚少,求閱讀源碼經驗分享。

    總結

    Angular與AngularJS都采用臟檢查的變化檢測機制,前者優(yōu)于后者主要體現(xiàn)在:

    • 單向數據流向
    • 以組件為單位維度獨立進行
    • 生產環(huán)境只進行一次檢查
    • 可自定義的變化檢測策略:?Default和onPush
    • 可自定義的變化檢測操作:markForcheck(),detectChanges(),detach(),?reattach(),checkNoChanges()
    • 代碼實現(xiàn)上的優(yōu)化,據說采用了VM friendly的代碼(這點我也不太明白,就隨便提一下)

    參考資料:

    • Tero Parviainen: Change And Its Detection In JavaScript Frameworks
    • Victor Savkin: Change Detection in Angular
    • Pascal Precht: Angular Change Dection Explained
    • Maxim Koretskyi:Everything you need to know about change detection in Angular
    • Maxim Koretskyi:Angular’s $digest is reborn in the newer version of Angular
    • Wojciech Kwiatek:Understanding Angular 2 change detection?
    • Ben Nadel: Change Detection Strategy Appears To Override The ChangeDetectorRef In Angular 2 RC 3
    • Juri: Tuning Angular’s Change Detection
    • Maxim Koretskyi:Everything you need to know about the?ExpressionChangedAfterItHasBeenCheckedError?error


    總結

    以上是生活随笔為你收集整理的Angular变化检测机制:改善的脏检查的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。