angular 2+ 變化檢測系列一(基礎概念)

什麼是變化檢測?

       變化檢測的基本功能就是獲取應用程序的內部狀態(state),而且是將這種狀態對用戶界面保持可見.狀態能夠是javascript中的任何的數據結構,好比對象,數組,(數字,布爾,字符串等基礎數據類型).這種狀態最終可能成爲用戶界面中的段落,表單,連接或按鈕,在web瀏覽器中咱們們稱之爲文檔對象模型(dom).將數據結構做爲輸入生成dom做爲輸出並展現給用戶,咱們稱這個過程爲渲染.javascript

       可是,在運行時發生更改時會變得更加棘手。一段時間後,DOM已經被渲染。咱們如何弄清楚模型中發生了哪些變化,以及咱們須要在哪裏更新DOM?訪問DOM樹老是很昂貴,所以咱們不只須要找出須要更新的位置,並且還但願儘量小地保持訪問權限.這能夠經過許多不一樣的方式解決。例如,一種方法是簡單地發出http請求並從新呈現整個頁面。另外一種方法是將新狀態的DOM與先前狀態進行區分並僅渲染差別的概念,這就是React使用Virtual DOM進行的操做.java

      所以,變動檢測的目標始終是預測數據及其變化。web

什麼致使變化發生?

      如今咱們知道變化檢測的所有內容,咱們可能想知道,什麼時候會發生這樣的變化? Angular何時知道它必須更新視圖?好吧,咱們來看看下面的代碼json

@Component({
  template: `
    <h1>{{firstname}} {{lastname}}</h1>
    <button (click)="changeName()">Change name</button>
  `
})
class MyApp {

  firstname:string = 'Pascal';
  lastname:string = 'Precht';

  changeName() {
    this.firstname = 'Brad';
    this.lastname = 'Green';
  }
}

      上面的組件只顯示兩個屬性,並提供了一種方法,能夠在單擊模板中的按鈕時更改它們。單擊此特定按鈕的那一刻是應用程序狀態發生更改的時刻,由於它會更改組件的屬性。那是咱們想要更新視圖的那一刻。數組

      又好比:瀏覽器

@Component()
class ContactsApp implements OnInit{

  contacts:Contact[] = [];

  constructor(private http: Http) {}

  ngOnInit() {
    this.http.get('/contacts')
      .map(res => res.json())
      .subscribe(contacts => this.contacts = contacts);
  }
}

       該組件包含聯繫人列表,在初始化時,它會執行http請求。一旦此請求返回,列表就會更新。一樣,此時,咱們的應用程序狀態已更改,所以咱們將要更新視圖。服務器

       應用程序狀態改變通常由如下3個方面引發:數據結構

  1. 事件: click,input,submit...
  2. XHR: 從遠程服務器獲取數據
  3. 定時器:setTimeout(),setInterval()

       事實證實,這三件事有一些共同之處。你能說出來嗎? ......正確!它們都是異步的.框架

       爲何你認爲這很重要?嗯......由於事實證實,當Angular真正對更新視圖感興趣時,這些是惟一的狀況。假設咱們有一個Angular組件,當單擊一個按鈕時它會執行一個處理程序:dom

@Component({
  selector: 'my-component',
  template: `
    <h3>We love {{name}}</h3>
    <button (click)="changeName()">Change name</button>
  `
})
class MyComponent {

  name:string = 'thoughtram';

  changeName() {
    this.name = 'Angular';
  }
}

        單擊組件的按鈕時,將執行changeName(),這將更改組件的name屬性,因爲咱們但願此更改也反映在DOM中,所以Angular將相應地更新視圖綁定{{name}}。很好,這彷佛神奇地工做.

        另外一個例子是使用setTimeout()更新name屬性。請注意,咱們刪除了該按鈕。咱們不是必須去作一些特殊的事情來通知框架狀態 發生了變化

@Component({
  selector: 'my-component',
  template: `
    <h3>We love {{name}}</h3>
  `
})
class MyComponent implements OnInit {

  name:string = 'thoughtram';

  ngOnInit() {
    setTimeout(() => {
      this.name = 'Angular';
    }, 1000);
  }
}

誰通知Angular進行變化檢測?     

  Angular容許咱們直接使用本機API。咱們不須要調用攔截器方法,所以Angular會通知更新DOM。這是純粹的魔法嗎? 背後的祕密就是Angular利用了Zones庫.Zones猴子補丁全局異步操做,如setTimeout()和addEventListener(),這就是Angular能夠輕鬆找到的緣由,什麼時候更新DOM. 簡短的版本是,在Angular的源代碼中,有一個名爲ApplicationRef的東西,它監聽NgZones onTurnDone事件。每當觸發此事件時,它都會執行tick()函數,該函數基本上執行更改檢測。

// very simplified version of actual source
class ApplicationRef {

  changeDetectorRefs:ChangeDetectorRef[] = [];

  constructor(private zone: NgZone) {
    this.zone.onTurnDone
      .subscribe(() => this.zone.run(() => this.tick());
  }

  tick() {
    this.changeDetectorRefs
      .forEach((ref) => ref.detectChanges());
  }
}

變化檢測執行機制

一個重要的事實是:咱們能夠爲每一個組件單獨控制如何以及什麼時候執行更改檢測

  因爲每一個組件都有本身的更改檢測器,而Angular應用程序由組件樹組成,所以邏輯結果是咱們也有一個更改檢測器樹。此樹也能夠視爲有向圖,其中數據始終從頂部流向底部.數據從上到下流動的緣由是由於每一個單獨的組件,從根組件開始,每一個組件也始終從上到下執行更改檢測。這很棒,由於單向數據流比循環更容易預測。相比之下,AngularJS採用的是雙向數據流,錯綜複雜的數據流使得它不得很少次檢查,使得數據最終趨向穩定。理論上,數據可能永遠不穩定。AngularJS給出的策略是,髒檢查超過10次,就認爲程序有問題,再也不進行檢查。咱們老是知道咱們在視圖中使用的數據來自何處,由於它只能來自其組件。在Angular 2+中,另外一個有趣的觀察是一次經過後變化檢測變得穩定。這意味着,若是咱們的某個組件在更改檢測期間第一次運行後致使任何其餘反作用,Angular將拋出錯誤。在開發模式下,Angular會進行二次檢查,若是出現上述狀況,二次檢查就會報錯:Expression Changed After It Has Been Checked Error。而在生產環境中,髒檢查只會執行一次。

 

    

變化檢測性能 

  默認狀況下,即便咱們每次都要檢查事件發生時每一個組件,Angular都很是快。它能夠在幾毫秒內執行數十萬次檢查。這主要是由於Angular生成了VM友好代碼。那是什麼意思?好吧,當咱們說每一個組件都有本身的變化檢測器時,它不像Angular中的這個通用的東西,它負責每一個組件的變化檢測。緣由是它必須以動態方式編寫,所以不管模型結構如何,它均可以檢查每一個組件。虛擬機不喜歡這種動態代碼,由於它們沒法對其進行優化。它被認爲是多態的,由於物體的形狀並不老是相同的。

  Angular在運行時爲每一個組件建立變化檢測器類,這些組件是單態的,由於它們確切地知道組件模型的形狀。 VM能夠完美地優化此代碼,從而使其執行起來很是快。好消息是咱們沒必要過多關心它,由於Angular會自動完成它。    

  本篇簡單介紹下angular 2+變化檢測的基礎,下一篇重點講一下變化檢測策略.

相關文章
相關標籤/搜索