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

  • 相关阅读:
    【Anagrams】 cpp
    【Count and Say】cpp
    【Roman To Integer】cpp
    【Integer To Roman】cpp
    【Valid Number】cpp
    重构之 实体与引用 逻辑实体 逻辑存在的形式 可引用逻辑实体 不可引用逻辑实体 散弹式修改
    Maven项目聚合 jar包锁定 依赖传递 私服
    Oracle学习2 视图 索引 sql编程 游标 存储过程 存储函数 触发器
    mysql案例~tcpdump的使用
    tidb架构~本地化安装
  • 原文地址:https://www.cnblogs.com/polk6/p/13687213.html
Copyright © 2011-2022 走看看