【Vue2】2. 文本響應式更新(響應式原理)

概述

Vue是數據驅動視圖模式,數據變動後,會自動更新視圖。開發很便利,不要手動管理視圖更新。哪 Vue 是如何實現自動更新,也就是響應式的?vue

以原生 JS 爲例,數據變化了須要手動獲取 Dom 節點,而後使用相關 Api 去修改視圖git

而 Vue 數據變化後須要自動更新視圖,因此流程應該:

從圖能夠大體感覺到,響應式更新有兩個難點須要解決:

  1. 如何監聽數據的變化?
  2. 如何觸發引用有變化數據的視圖鏡像更新?

代碼實現, 一共 373 行JS代碼,包含Watcher/Observer的簡單實現github

DEMO展現

入口代碼:數據結構

new Vue({
  el: '#app',
  data() {
    return {
      message: 'hello'
    }
  },
  render(h) {
    return h('div', null, this.message)
  },
})
複製代碼

渲染效果: 閉包

響應式更新:app

監聽數據變化

通常而言,對於基本類型的值是否變化,能夠對比先後值是否相同。對於引用類型是否有變化,則可能須要遞歸判斷屬性是否都一致。異步

在 Vue 中,組件數據 data 是 Object 類型的,這樣作的好處,我理解一是方便擴展屬性、二是爲了能方便對數據進行攔截、代理等。函數

針對 Object 類型的的數據,其相關key/value能夠經過 數據屬性/訪問器屬性 獲取。二者能夠是能夠互換的: 性能

數據屬性訪問簡單直觀,而訪問器屬性擴展性更好。在Vue種爲了監聽屬性變化,使用訪問器屬性 覆寫了 數據屬性。

在Vue中定義的數據結構爲 ui

通過Vue解析處理後 data 函數的值會存儲到 _data 私有屬性中:
Vue 也是藉助 Object.defineProperty 實現的:

function proxy(target, sourceKey, key) {
  Object.defineProperty(target, key, {
    enumerable: true,
    configurable: true,
    get() {
      return target[sourceKey][key]
    },
    set(val) {
      target[sourceKey][key] = val
    }
  })
}
複製代碼

藉助 getter/setter 便可感知到數據變化,而自動觸發視圖更新作準備

數據關聯視圖(依賴收集)

Vue視圖渲染的過程爲: 模板 + 數據 -> 虛擬Dom -> Diff ... Dom操做 -> 真實Dom(渲染到頁面中),這個過程能夠很天然的使用數據。但數據不只包含視圖展現數據、還會有一些視圖不須要數據,若是每次檢測到數據變化都從新執行一遍視圖的渲染,這樣可能形成極大的性能損耗。那如何才能識別哪些是視圖所依賴的數據呢?

數據是在函數中引用的,故能夠在訪問數據時,記錄當前執行的函數便可,整個思路以下:

簡單實現步驟爲:

  1. 設置一個全局變量記錄當前執行的 render 函數
  2. render 函數會引用data,屬性的 getter 訪問器,在經過閉包保存當前的執行的 render 函數(此時記錄在 全局變量中)
  3. 將全局變量置空
  4. 數據變化時,會觸發屬性的 setter

訪問器,執行閉包的保存的render函數,觸發視圖從新渲染 在Vue中實現,爲了更好的性能(異步渲染,可能多處修改屬性值)、更好的擴展性(屬性可能多處被引用,須要自動更新、以及支持watch、compute屬性等),引入了 watcher/observer/dep 來實現依賴收集以及自動更新。

經過更多的分層,實現更好的擴展性。代碼的思路:

  1. initData 屬性時,對屬性進行 observe 處理 <= observer概念
  2. mount 時,使用watcher管理更新
  3. 渲染時,設置一個全局變量,被引用屬性的 getter 屬性會經過該所有變量 收集對應的依賴
  4. 數據更新時,觸發watcher 的 update 方法進行從新渲染操做

總結

經過 defineProperty 對數據屬性進行攔截,再攔截的基礎上 基於 JS 單線程原理進行依賴收集,數據變動時觸發視圖的從新渲染。

相關文章
相關標籤/搜索