Vue2 原始碼閱讀(三) 雙向繫結原理
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.1Observer類
說明:深度遍歷data內的成員,新增觀察者模式,具體為以下操作:
①當成員型別為Object或Array時,在根節點建立一個__ob__成員,指向一個初始化的Observer類的例項。
②當成員不是Object和Array時,封裝其set和get屬性,在2個屬性的內部增加對Dep支援,用來支援觀察者模式。當進行get操作時觸發depend(),進行set操作時觸發notify()。
2.2Dep類
說明:在每一個需要進行繫結(單項或雙向)的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.3Watcher類
說明:Watcher類,即訂閱者,在Vue中扮演的角色是當監聽到data的值變更時,進行相關工作,比如修改HTML程式碼、getter、watchapi等等。
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.4callHook(vm, 'beforeMount')
說明:觸發beforeMount事件的回撥。
5.5 new Watcher
說明:建立一個Watcher類的例項,並賦值給Vue例項的_watcher成員。
Watcher類,即監聽者類,在Vue中扮演的角色是當監聽到data的值變更時,進行相關工作,比如修改HTML程式碼、getter、watchapi等等。
5.6 vm._render()
說明:呼叫例項的render()轉換為VNode。
這一塊比較複雜,其最終結果是把ASTtree→轉換為VNodetree。
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開發之路系列文章 選單載入中...