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事件的回调。

 

posted on 2020-11-22 21:15  FangMu  阅读(553)  评论(0编辑  收藏  举报