Angular2的雙向數據綁定

什麼是雙向綁定

如圖:typescript

 
雙向綁定.jpg

雙向綁定機制維護了頁面(View)與數據(Data)的一致性。現在,MVVM已是前段流行框架必不可少的一部分。angular2

Angular2中的雙向綁定

雙向綁定,也是Angular2的核心概念之一,Angular2的雙向綁定是這樣的:app

  • data=>view:數據綁定,模板語法是 []
  • view=>data:事件綁定,模板語法是 ()
  • Angular其實並無一個雙向綁定的實現,他的雙向綁定就是數據綁定+事件綁定,模板語法是 [()] 。

Angular2官方給的例子:框架

<!--value是數據綁定,input是事件綁定--> <input [value]="currentHero.name" (input)="currentHero.name=$event.target.value" > <!--等價--> <input [(ngModel)]="currentHero.name"> 

上面是input空間的雙向綁定語法,很清楚的說明了雙向綁定與兩個單向綁定的關係。這裏沒有使用ngModule語法,ngModule語法內部實現與這個差很少。異步

事件綁定

  1. 用戶操做出發DOM事件通知
  2. Angular監聽到了通知,而後執行模板語法,上面的例子就是將input控件的輸入值賦給了currentHero.name

數據綁定

因爲js語言並無屬性變化通知的機制,因此angular也不知道誰發生了變化,在何時變了。Angular的變化機制是:函數

 
image.png

上面的例子中input的數據綁定過程以下:組件化

  1. 代碼修改了currentHero.name的值。
  2. 觸發整個組件樹的變化檢查。
  3. input顯示了修改後的值。
數據什麼時候變化

主要入下集中狀況可能改變數據:性能

  • 用戶輸入操做,好比點擊,提交等。
  • 請求服務端數據。
  • 定時事件,好比setTimeoutsetInterval

這幾點有個共同點,就是他們都是異步的。也就是說,全部的異步操做是可能致使數據變化的根源因素。優化

如何通知變化

在Angularjs中是由代碼$scope.$apply()或者$scope.$digest觸發,而Angular2接入了ZoneJS,由它監聽了Angular全部的異步事件。ZoneJS重寫了全部的異步API(所謂的猴子補丁,MonkeyPath)。ZoneJS會通知Angular可能有數據發生變化,須要檢測更新。ui

變化檢測原理 -- 髒檢查

所謂髒檢查就是存儲全部變量的值,每當可能有變量發生變化須要檢查時,就將全部變量的舊值跟新值進行比較,不相等就說明檢測到變化,須要更新對應的視圖。

AngularJS與Angular2變化檢測的區別

Angularjs的變化檢測機制也是髒檢查,而Angular2的變化檢測性能比Angularjs提高了不少。

Angular2

Angular的核心是組件化,組件的嵌套會使得最終造成一棵組件樹。Angular的變化檢測能夠分組件進行,每一個組件都有對應的變化檢測器ChangeDetector。可想而知,這些變化檢測器也會構成一棵樹。

另外,Angular的數據流是自頂而下的,從父組件到子組件單向流動。單向數據流向保證了高效、可預測的變化檢測,儘管檢查了負組件以後,自組件可能會改變父組件的數據使得父組件須要再次被檢查,這是不被推薦的數據處理方式。在開發模式下,Angular會進行二次檢查,若是出現上述狀況,二次檢查就會報錯:ExpressionChangedAfterItHasBeenCheckedError(關於這個問題的答案,能夠在參考資料中找到)。而在生產環境中,髒檢查只會執行一次。

Angularjs

相比之下,Angularjs採用的是雙向數據流,錯綜複雜的數據流使得他不得很少次檢查,使得數據最終趨向穩定。理論上,數據永遠不可能穩定,Angularjs的策略是,髒檢查超過10次就認定程序有問題。

 
angular2-change-detection-moscowjs-31-9-638.jpg

變化檢測優化

優化策略

有2個思路:

  1. OnPush策略:我知道我沒變,別查我。
  2. 手動控制刷新:我變了,只查我。

變化檢測策略 OnPush

Angular還讓開發者擁有制定變化策略的能力。

export enum ChangeDetectionStrategy { OnPush, // 表示變化檢測對象的狀態爲`CheckOnce` Default, // 表示變化檢測對象的狀態爲`CheckAlways` } 

ChangeDetectionStrategy能夠看到,Angular有兩種變化檢測策略。Default是Angular默認的變化檢測策略,也就是髒檢查(只要有值發生變化,就所有檢查)。開發者能夠根據場景來設置更加高效的變化檢測方式:OnPushOnPush策略,就是隻有當輸入數據的引用發生變化或者有事件觸發時,組件進行變化檢測。

@Component({ template: ` <h2>{{vData.name}}</h2> <span>{{vData.email}}</span> `, // 設置該組件的變化檢測策略爲onPush changeDetection: ChangeDetectionStrategy.OnPush }) class VCardCmp { @Input() vData; } 

好比上面這個例子,當vData的屬性值發生變化的時候,這個組件不會發生變化檢測,只有當vData從新賦值的時候纔會。通常,只接受輸入的木偶子組件(dumb components)比較適合採用onPush策略。

那何時只要對象的屬性值發生變化,整個對象的引用就變了呢?不可變對象(Immutable Object)。當組件中的輸入對象是不變量時,可採用onPush變化檢測策略,減小變化檢測的頻率。換個角度來講,爲了更加智能地執行變化檢測,能夠在只接受輸入的子組件中採用onPush策略。

手動控制變化檢測

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

  • markForCheck():將檢查組件的全部父組件全部子組件,即便設置了變化檢測策略爲onPush
  • detach():將變化檢測對象脫離檢測對象樹,再也不進行變化檢查;結合detectChanges可實現局部變化檢測。(採用onPush策略以後的組件detach()無效)
  • detectChanges():將檢測該組件及其子組件,結合detach可實現局部檢測。
  • checkNoChanges(): 檢測該組件及其子組件,若是有變化存在則報錯,用於開發階段二次驗證變化已經完成。
  • reattach():將脫離的變化檢測對象從新連接到變化檢測樹上。

那麼,若是是Observable的話,它會訂閱全部的變量變化,只要在訂閱回調函數中手動觸發變化檢測便可實現最小成本的檢測(仍採用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++; // 數據模型發生變化 this.cd.markForCheck(); // 手動觸發檢測 }) } } 

另外,當數據模型變化太過頻繁,咱們可自定義變化檢測的時機。舉個例子:

@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(); // 脫離變化檢測樹 } } } 

總結

Angular與Angularjs都採用變化檢測機制,前者優於後者主要體如今:

  • 單項數據流動
  • 以組件爲單位維度獨立進行檢測
  • 生產環境只進行一次檢查
  • 可自定義的變化檢測策略:DefaultonPush
  • 可自定義的變化檢測操做:markForcheck()detectChanges()detach()reattach()checkNoChanges()
  • 代碼實現上的優化,聽說採用了VM friendly的代碼。

連接:https://www.jianshu.com/p/cee44e8831c9

相關文章
相關標籤/搜索