現代前端框架的變更檢查機制

目的

  • 了解現代 framework 要解決的問題
  • 從另一個角度比較各個現代 framework
  • Not a tutorial

為何需要現代前端框架?

  • 避免頁面 flash
  • 前後端分離
  • 只關注 Data 操作
    • 去除重複瑣碎的 DOM 操作

為何需要現代前端框架

  • 避免頁面 flash
  • 前後端分離
  • 只關注 Data 操作
    • 去除重複瑣碎的 DOM 操作
    • 把 Data 投影 到 UI(DOM) 上

只關注 Data 操作的好處

  • view & business logic 間的關注點分離
  • 減少瑣碎的 DOM 操作
  • Data 即畫面

把資料對應到畫面上的難點

  • 第一次 render 都很簡單,把 Data 都全部顯示到 UI 上就好
    • server side rendering
      • 所有畫面都從 server 來,並且都是一次性的
      • 前端不需要管理狀態
  • 但如果涉及 Data (狀態) 的改變就沒那麼容易
    • 要怎麼知道開發者改了甚麼 data?
    • 不知道那些 data 改變的話,如何把變更投影到 UI 上?

把改變的 Data 投影到 UI 上的方法

  • 不知道啥 data 改變,每個都檢查過就知道了

    • AngularJS

    • Angular

  • 才不管啥 data 改變,全部重 render 就對了

    • React

  • 我知道啥 data 改變了,因為被改變時會收到通知

    • Vue

把改變的 Data 投影到 UI 上的方法

  • 不知道啥 data 改變,每個都檢查過就知道了

    • AngularJS

    • Angular

  • 才不管啥 data 改變,全部重 render 就對了

    • React

  • 我知道啥 data 改變了,因為被改變時會收到通知

    • Vue

不知道啥 data 改變,每個都檢查過就知道了

  • Dirty Checking

    • ​AngularJS

    • Angular

AngularJS - Dirty Checking

  • 機制:

    • 所有 watcher 都檢查 data 有沒有改變 (digest loop)

      • template/$watcher 都算是 watcher

    • 每次會執行 2~10 次的 digest loop 來檢查值

      • 因為 child 也可能改變 parent

        • Graph, not Tree

<h1>Hello {{yourName}}!</h1>

AngularJS - Dirty Checking

  • 什麼時候檢查?

    • 每個非同步動作被觸發的時候就檢查

    • e.g. 點擊,XHR,timeout ...etc

    • API:

      • template: 提供 ng 開頭的 api 來觸發檢查

      • js: 提供 $ 開頭的 api 來觸發檢查

//js api example
$timeout(function() {
    $scope.greetings = "Hello World!";
}, 5000);
//html api example
<button ng-click="greeting()">Hello</button>

Angular - Dirty Checking

  • 機制:

    • 所有 watcher 都檢查 data 有沒有改變 (digest loop)

    • 只會由上往下檢查一次

      • 規定 child 不能影響 parent,否則會忽略/報錯
      • unidirectional flow
      • ​Tree, not Graph

Angular - Dirty Checking

  • 什麼時候檢查?

    • 每個非同步動作被更新的時候就檢查

    • e.g. 點擊,XHR,timeout ...etc

    • API:

      • template: (event)

      • js: Angular 會 patch 所有的非同步 API

        • 每個非同步 API 中都加上 hook 通知 Angular 更新

        • Zone.js

        • 因此無須額外的 js API 可以觸發檢查

//html api example
<button (click)="greeting()">Hello</button>

Angular vs. AngularJS

  • Unidirectional flow

  • 減少 API

  • AOT (Ahead of Time) 技術

  • ​優化程式碼,compiler friendly code
  • 減少動態語法

Angular vs. AngularJS

  • Unidirectional flow

  • 減少 API

  • AOT 技術

在 Change Detection 的速度上快了 3~10x

把改變的 Data 投影到 UI 上的方法

  • 不知道啥 data 改變,每個都檢查過就知道了

    • AngularJS

    • Angular

  • 才不管啥 data 改變,全部重 render 就對了

    • React

  • 我知道啥 data 改變了,因為被改變時會收到通知

    • Vue

才不管啥 data 改變,直接全部重 render

  • React

    • 只要狀態改變,就整個 Component 重 render

      • 效仿 Server Rendering

      • 因此不需要關注 Data 改變的差異

      • Data 改變就全部重 render

    • Performance?

      • ​DOM 操作很慢

        • parse、repaint、reflow

      • 重複 render 沒改變的畫面很浪費

才不管啥 data 改變,直接全部重 render

  • Performance?
    • Virtual DOM

React - Virtual DOM

  • 機制:

    • React render 時先呼叫 virtual DOM render

    • 以 JS 物件模擬 DOM 結構

      • 即 virtual DOM

    • 與先前的 virtual DOM 做 diff

    • 把 diff 的部分 patch 回真實的 DOM 中

React - Virtual DOM

  • 用途:

    • 為 render 到 DOM 前的緩衝層

    • 可想成 DOM 層面的變更檢查機制

  • 優點:

    • 實際上只會讓 DOM 去 re-render 有 diff 的部分

    • 解決全部重 render 會拖慢速度的問題

 

React - Virtual DOM

  • 什麼時候 Render Virtual DOM?

    • ReactDOM.render

      • 通常整個 APP 只會有一個

    • component.setState

 

// render api example
ReactDOM.render(
  <h1>Hello, world!</h1>,
  document.getElementById('root')
);
// setState api example
this.setState({greetings: 'Hello World'});

把改變的 Data 投影到 UI 上的方法

  • 不知道啥 data 改變,每個都檢查過就知道了

    • AngularJS

    • Angular

  • 才不管啥 data 改變,全部重 render 就對了

    • React

  • 我知道啥 data 改變了,因為被改變時會收到通知

    • Vue

我知道啥 data 改變了,因為被改變時會收到通知

  • Vue
    • 依賴收集
      • 觀察者模式

Vue - 依賴收集

  • 機制:

    • 使用 Data 內部的提供 getter & setter

      • Object.defineProperty

    • Data 被 get 時,會把 get 他的 watcher 當成 subscriber

    • Data 被 set 時,會 notify 他的 subscribers

    • watcher 觸發 virtual DOM 的 render

<h1>Hello {{yourName}}!</h1>

Vue - 依賴收集

  • 要觀察哪些 data?

  • API:

    • new Vue

<h1>Hello {{yourName}}!</h1>

var vm = new Vue({
  el: '#app2',
  data: {
    yourName: 'Henry'
  },
});

把改變的 Data 投影到 UI 上的方法

  • 不知道啥 data 改變,每個都檢查過就知道了

    • AngularJS

    • Angular

  • 才不管啥 data 改變,全部重 render 就對了

    • React

  • 我知道啥 data 改變了,因為被改變時會收到通知

    • Vue

結論

  • 如何 變更偵測 是各個 framework 致力於解決的最大問題
  • 變更偵測的方式會影響到 framework API 的設計
  • 每個 framework 都使用了一些很 hack 的手法來解決問題

References

Change Detections in modern Front End Frameworks

By Chang Henry

Change Detections in modern Front End Frameworks

  • 104