Vue源碼探究-數據綁定邏輯架構

數據觀察系統是Vue實現數據綁定、異步更新的核心模塊,數據觀察系統的實現也是Vue源碼裏最爲複雜的部分,在仔細研究具體實現以前,先對整個數據綁定的邏輯架構進行一個充分的認識,會更有助於解讀源碼。設計模式

先說明一下,由於三個類的名稱比較容易讓人誤解,因此在之後把Observer稱做觀察目標,Watcher稱做監視器,Dep稱做依賴對象。數組

數據綁定邏輯架構

Vue的數據觀察系統是基於發佈者/訂閱者模式,數據更新觸發刷新頁面的過程主要依賴數據觀察系統裏鐵三角關係。在這個系統中,主要角色分別是 ObserverDepWatcher 這三個對象,對於每個角色在觀察數據更新的流程中各自承擔的職責須要深刻進行理解。下面請出三個主角登場,來介紹一下它們:架構

Observer

Observer 至關於觀察目標類,在數據綁定邏輯架構中的職責是收集須要觀察的數據對象,進行變量存取器的包裝,並遞歸地對每個須要觀察的對象註冊發佈者對象,再由發佈者去註冊相應的監視器。這裏很是巧妙的是觸發通知監視器數據更新的事件的註冊,通常的發佈訂閱模式須要創建一個事件管理器或者調度中心來統一管理各類事件的註冊,然而Vue的數據綁定不須要這樣的機制,它借用 Object.defineProperty 方法來爲每個被監視的數據設置了存取器,依靠數據的存取行爲天然地實現了事件的觸發。在初始化Vue實例中設置的 data 屬性時,對這些輸入的數據對象對行了依賴追蹤,包裝後的變量存放在 _data 屬性中,這個過程當中發佈者和監視器的依賴添加是不可見的;而經過配置 watch 屬性顯式設置的監視器,就能夠在實例的 _watchers 私有屬性中查看到。每一個組件初始化後有一個惟一的 _watcher 對象,它是一個用來監視在 data 中註冊的數據變更從而更新視圖的監視器,它也默認被添加到了各屬性的依賴監視數組中。在每一個修改成可觀察狀態的屬性中,都含有一個 Dep 實例即發佈者,這個對象的 subs 屬性就是用來存放依賴的全部監視器 Watcher 實例對象,subs 能夠理解爲訂閱者,即全部訂閱了該數據對象變更的監視器的數組集合。之因此須要在一開始爲數據收集依賴,參考另外一些開發者的總結是因爲並不是全部的數據都值得監視,要知道監視沒有用到的數據就是對性能的浪費,在實例觀察中也確實發現,頁面中沒有用到的屬性,沒有被初始化爲依賴項,這樣即使改變了它的數值,頁面也不會觸發多餘的刷新。異步

Dep

Dep 在Vue的數據觀察者系統裏充當發佈者的角色,它不只用來觸發數據更新和創建依賴的事件,還用來存放每個可監視數據所依賴的監視器,這個正是在第一步收集依賴時的重要一環。實例初始化的過程當中收集了全部須要跟蹤變化的數據,在運用 Observer 從新包裝每個屬性的同時,建立了各自的 dep 對象,並在get和set方法中分別使用了 Dep的兩個方法:depend 創建依賴,notify 通知變更。另外 Dep 還負責維護依賴監視器的增減。在構造 Dep 類的過程當中,定義了全局的 Dep.target 對象和 targetStack 數組,targetStack 數組是用來存放待執行的 watcher 棧,Dep.target 是用來指代當前的監視器,必須惟一,它的存在對於創建監視器的依賴起到重要做用,在重置數據的 getter 時,當它存在時才執行創建數據與監視器的依賴,即只有顯式配置了 watch 或建立了 computed 變量時纔會在實例的私有屬性裏看到監視器。性能

Watcher

Watcher 是這個架構中的監視器,充當觀察者的角色。在Vue實例初始化的過程當中,必定會默認建立一個監視器,這個監視器就是用來監視實例對象的數據變化用來更新視圖的,實例的私有屬性 _watcher 用來存放它。在建立可觀察的數據時,每個數據的 Dep 對象會收集監視器並創建依賴,當數據變化時,Dep 對象通知全部的監視器執行更新,執行更新有兩種模式,若是依賴是經過配置 computed 變量建立的,則會當即觸發相關的更新操做,若是數據的 dep.subs 數組中沒有依賴的監視器,則默認惰性更新模式。Watcher 類最主要的做用是通知視圖更新,衆所周知視圖的更新是很是花費時間,會影響程序性能,爲了儘可能減小視圖更新致使的性能損失,在通知視圖執行更新操做以前會有一個緩衝時段,在這個時段中會收集最後一次監視器收到的變動,減小沒必要要的重複更新,實現最優性能。學習

架構圖示

充分了解了數據觀察系統的三個主角以後,再來看看官網貼出的示意圖,就會發現終於能摸清Vue的數據觀察系統的架構了,只不過渲染視圖的具體實現與數據觀察系統的交互暫時尚未去摸索,之後會仔細地去探索,如今終於比較清晰地弄懂了Vue的數據綁定的原理了。ui

Vue數據觀察系統架構

一個簡單的實例

爲了更清晰初步瞭解數據綁定相關的初始化過程,建立了一個很是簡單的實例,data配置了兩個屬性,其中 name 變量並不在頁面中使用,還顯式設置了一個依賴 msg 的監視器。spa

new Vue({
  data () {
    return {
      msg: 'hello',
      name: ''
    }
  },
  watch: {
    'msg' (value) {
      console.log('msg更新了')
    }
  }
})
複製代碼

下面截圖是實例的相關監視器私有屬性,_watcher 是跟蹤頁面渲染的監視器,每一個實例惟一;_wacthers 是實例所擁有的全部監視器的集合。顯式設置的 watcher 在是數組中的第一個對象。這裏雖然看不到 Observer 背後的包裝過程,但改變了 msg 屬性以後,能夠看到監視器執行的回調顯示。.net

相關的私有屬性

視圖更新監視器

顯式設置的watcher


從Vue對象實例化着手到開始分析數據綁定的核心實現,這一路過來尚未真正遇到值得困擾的問題。但不曾想到的是,數據綁定這個Vue的核心特點功能居然讓我苦苦研讀了好幾天,彷佛之前對於設計模式的瞭解顯得那樣無力。期間去搜索了一些前人作的分析說明文章以求從各個角度深刻理解,但大多數解讀讀完後依然以爲沒能很透徹地理解這個模塊,後來讀到了一個簡易實現Vue觀察者系統的文章,讓我突然對核心邏輯是如何實現的有了比較清晰的認識,並且對於設計模式也有了更深刻的理解。也許第一次讀源碼的時候太多非核心的技術實現干擾了對於核心部分的理解,也由於以前的一些知識不牢固,因此從這一次學習中獲得了一個很好的經驗,要更加關注本質。設計

相關文章
相關標籤/搜索