1. 前言
Vue的雙向系結一直是其核心,在這里我們將通過Vue原始碼來了解雙向系結的原理,
vue版本:2.6.11
vue倉庫:https://github.com/vuejs/vue
vue檔案:https://cn.vuejs.org/
2. Observer 觀察者模式
簡單來講,觀察者模式(Observer Pattern)適用于物件間存在一對多關系,比如,當一個物件被修改時,會自動通知依賴它的物件(事件),
Vue的雙向系結就是采用此模式進行的,當某個物件的值被更改時,依賴(參考)此值的物件或事件都會收到通知,
觀察者模式的更多資訊可參考:https://www.runoob.com/design-pattern/observer-pattern.html
Vue代碼中創建了 Observer類、Dep類、Watcher類來構建觀察者模式,
2.1 Observer類
說明:深度遍歷data內的成員,添加觀察者模式,具體為以下操作:
①當成員型別為Object或Array時,在根節點創建一個 __ob__ 成員,指向一個初始化的Observer類的實體,
②當成員不是Object和Array時,封裝其set和get屬性,在2個屬性的內部增加對Dep支持,用來支持觀察者模式,當進行get操作時觸發depend(),進行set操作時觸發notify(),

2.2 Dep類
說明:在每一個需要進行系結(單項或雙向)的js物件(非Object和Array),Vue都在其物件內創建了一個Dep類的實體,此Dep實體用于管理當前js物件與訂閱者的關系,
當進行get操作時觸發Dep的depend(),其內部邏輯為此
進行set操作時觸發Dep的notify(),在內部遍歷訂閱者集合,呼叫每個訂閱者的update()方法,

主要成員:
subs[]:管理一個訂閱者集合,
addSub():添加訂閱者;
removeSub():移除訂閱者;
notify():遍歷訂閱者陣列,呼叫每個訂閱者的update()方法,
depend():添加dep與sub(訂閱者)的互相依賴,sub(訂閱者)的dep集合添加此dep,dep實體的sub集合添加相關訂閱者,
2.3 Watcher類
說明:Watcher類,即訂閱者,在Vue中扮演的角色是當監聽到data的值變更時,進行相關作業,比如修改HTML代碼、getter、watch api等等,
3. 什么是data
在Vue官方教程對data解釋如下:data為Vue 實體的資料物件,Vue 將會遞回將 data 的 property 轉換為 getter/setter,從而讓 data 的 property 能夠回應資料變化,
物件必須是純粹的物件 (含有零個或多個的 key/value 對);瀏覽器 API 創建的原生物件和原型鏈上的 property 會被忽略,大概來說,data 應該只能是資料,
首先看個簡單例子:

<body>
<div id="app">
<p>姓名:<input v-model="userName" /></p>
<p>年齡:<input v-model="age" /></p>
<span>{{userName}}</span>
</div>
<script>
var VM = new Vue({
data: {
userName: '',
age: '',
likes: ['語文', '數學']
},
el: '#app'
})
</script>
</body>
4. data是如何初始化的
通過Vue原始碼,data在Vue初始化時經過了以下幾個步驟:

4.1 initData(vm)
說明:這一步中,Vue會遍歷data的成員,分別系結到Vue實體的_data成員和根節點上,
其中根節點data成員的set和get屬性都是與_data相關,

示例:

4.2 observe(data)
同 #2.1 Observer類 的解釋:深度遍歷data內的成員,添加觀察者模式,具體為以下操作:
①當成員型別為Object或Array時,在根節點創建一個 __ob__ 成員,指向一個初始化的Observer類的實體,
②當成員不是Object和Array時,封裝其set和get屬性,在2個屬性的內部增加對Dep支持,用來支持觀察者模式,當進行get操作時觸發depend(),進行set操作時觸發notify(),

5. HTML是如何跟data聯動的
Vue原始碼對HTML處理的步驟如下:
5.1 $mount(el)
說明:獲取初始化Vue物件的el成員指定的HTML,
5.2 parse(html)
說明:將HTML代碼轉換為AST物件,AST即抽象語法樹,這里不做深入講解,感興趣的可自行搜索,
在這一步中,會得到HTML元素內的屬性,包括Vue中的指令,

5.3 generate(ast)
說明:深度遍歷AST物件,生成render方法,
注意:此時render為string型別,(這里方便展示進行了代碼格式化)

5.4 callHook(vm, 'beforeMount')
說明:觸發beforeMount事件的回呼,
5.5 new Watcher
說明:創建一個Watcher類的實體,并賦值給Vue實體的_watcher成員,
Watcher類,即監聽者類,在Vue中扮演的角色是當監聽到data的值變更時,進行相關作業,比如修改HTML代碼、getter、watch api等等,

5.6 vm._render()
說明:呼叫實體的render()轉換為VNode,
這一塊比較復雜,其最終結果是把AST tree → 轉換為VNode tree,

5.7 vm._update()
說明:新生成的VNode替換舊的VNode,替換演算法就是diff演算法,而在初始化時,原始html代碼會轉換為一個空VNode
關于diff演算法的了解可看此篇文章:
Vue的diff演算法決議:https://www.infoq.cn/article/uDLCPKH4iQb0cR5wGY7f
5.8 callHook(vm, 'mounted')
說明:觸發mounted事件的回呼,
End Web開發之路系列文章 選單加載中...
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/226456.html
標籤:JavaScript
