1. 程式人生 > 實用技巧 >Vue2 原始碼閱讀(三) 雙向繫結原理

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開發之路系列文章 選單載入中...