zoukankan      html  css  js  c++  java
  • 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.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事件的回调。

  • 相关阅读:
    每日日报8月12日
    每日日报8月15日
    每日日报8月18日
    每日日报8月9日
    九月29号——动手又动脑
    今日总结
    每周总结
    今日总结
    周总结
    今日总结
  • 原文地址:https://www.cnblogs.com/polk6/p/13687213.html
Copyright © 2011-2022 走看看