美文网首页我爱编程
Vue的双向数据绑定原理

Vue的双向数据绑定原理

作者: 钱罗罗_ | 来源:发表于2018-01-17 17:39 被阅读1561次

    1.目前双向数据绑定的方法

        发布者-订阅者模式(backbone.js)

            因为本文研究的不是这个,所以不做详细解释。

        脏检查(Angular.js)

            angular只在指定的事件触发时进入脏值检测,例如:DOM事件、XHR响应事件、location变更事件、timer事件、执行$digest()/$apply()

        数据劫持(vue)

            vue是采用数据劫持$发布者-订阅者结合的方式,来做数据绑定,核心就是Object.defineProperty(),劫持各个属性的getter和setter,在数据模型变化的时候,发布消息给订阅者(绑定了数据模型的DOM元素),触发相应的监听回调。

      

    2.简单例子实现数据绑定

    数据绑定的demo

        obj.name根据输入框值得变化改变,视图会根据obj.name的改变进行更新。这就是简单的实现了view=>model和model=>view的双向数据绑定。这是vue的基本原理,但是在vue 中并不是这么实现的。

    3.vue实现数据绑定的能分解步骤

    (1)要将视图模型和数据模型绑定起来

    (2)视图变化时,触发数据模型变化

    (3)数据模型变化时,触发视图变化

    4.流程图

    vue双向数据绑定流程图

        在实例化一个Vue对象的时候,会传进去一个data对象,之后分成两个进程,一个进程是对挂载目标元素模板里的v-model和{{ }};两个指令进行编译。另一个进程是对传进去的data对象里面的数据进行监听。

        上图中,observe是利用Object.defineProperty()对传入的data对象进行数据监听,在数据改变的时候触发该属性的set方法,更新该属性的值,并发布消息,我(该属性)的值变了。

        compile是编译器,找到vue的指令v-model所在的元素,将data中该属性的值赋给元素的value,并给这个元素添加二级监听器,在元素的值改变的时候,将新值赋给data里面同名属性,这个时候就完成了单向数据绑定,视图 >> 模型。

        那么最终的由模型到视图的更新,依赖于dep和watcher,dep会收集订阅者,就是绑定了data里面属性的元素,在数据更新的时候,会触发该属性的set方法,在set里触发该属性的消息发布通知函数。而Watcher根据收到的数据变化通知,更新相应的数据。

        dep这个东东给大家解释一下,就是data里的每个属性都有一个dep对象,dep对象里可以有很多订阅者(watcher),但是只有一个添加订阅者的方法和一个发布变化通知的方法,就是模板上可以有多处元素绑定data里的同一个属性值,所以dep是依赖于data里面的属性的。

        而Watcher是每个{{ }}有一个,初次编译的时候,会在new的时候自动更新一下模板的数据,等到下次数据改变的时候,由dep通知数据更新,直接调用watcher的update方法,更新模板的绑定数据。

    5.new Vue()都干了什么

    实例化vue

        上面是正常在构建一个vue项目的时候,实例化一个vue。

    Vue类

        实例化一个Vue的两个进程,就是数据监听和模板编译。observe(data, obj)传入两个参数,一个是数据模型data,一个是vue的实例vm。

        而nodeToFragment方法就是识别模板里面的vue指令进行相应的处理,处理之后的template插入到挂载目标元素里。

    6.Object.defineProperty(obj, key, options)    

    先讲一下这个方法,为下面的数据监听做铺垫。

    很简单,该方法接受三个参数,全部都是必填的。参数:       

     obj:目标对象        

     key:属性或者方法的名字       

     options:目标属性所拥有的特性   

    主要解释第三个参数        {            

        value:属性的值,            

        writable:布尔值,规定属性是否可重写,          

        configurable:总开关,以第一次设置为准,一旦是false,则其他属性不可设置,            

        enumerbale:决定属性是否可枚举            

        get:重点,后面讲,            

        set:重点,后面讲       

     }    

    因为如果没有明确的设置其他值,默认都是false。(可以这么理解)。

    configurable    只能设置一次,第二次设置会报错。

    writable    在值为true的情况下,才能对该值进行重写修改。

    enumerable    定义属性是否可枚举   ,就是能不能被遍历出来。

    访问器:(set\get)不能和writable/value同时设置    就是会冲突的意思,set和writable都是设置属性值的,所以会冲突    而get和value都是获取属性值的,所以也会冲突(可以先这么理解)    

    so:一山不容二虎,要么用访问器,要么用writable和value.

    set    是一个函数,接收一个新值,会在值被重写或修改的时候触发这个函数

    get    是一个函数,返回一个值,会在属性被调用的时候触发。

    7.数据监听

        利用上面的方法,将data里面所有的属性值遍历一遍,添加上set和get访问器,这样在设置data的属性值的时候,会触发set方法,那么set方法主要有两个作用,一是改变data里面的属性值,二是发出数据变化的通知。

    data数据遍历 data处理函数

        上面的方法中,为每一个属性值绑定一个dep对象,初次调用会有Dep.target,当然这是在Watcher里面进行调用,暂且不谈。属性的两个方法,一个get和一个set,数值改变的时候,首先替换掉旧值,再进行数据变化通知,因为变化通知的是订阅这个属性的元素,所以将订阅这个属性的管理对象dep传进去就好了。

    通知函数

        通知函数调用了当前dep对象的notify函数,来通知该dep管理的所有订阅者。至此实现了data数据的监听。

    dep管理订阅者对象

    8.模板编译

        浏览器不认识vue指令,所以要对模板里面的vue指令进行编译,那么先要获取花在目标,将里面的所有节点劫持下来,在新建的文档碎片(documentflagment)里面进行编译,编译完成自后再插入到挂载目标节点。

    处理模板函数

        如果挂载目标元素有子节点,那么对第一个子节点进行编译(PS:vue规定template标签里只能有一个子元素哦,所以页面的构建要由一个标签包裹起来,在放入template里面)

    模板编译器

        模板编译器函数会判断元素节点是什么类型,表单元素和文本节点分别处理,因为文本节点的value会由用户手动输入,所以要给表单元素添加监听器,根据输入的value改变data里的属性值,再更新到视图上,这是双向的数据流。而文本节点,只要改变data里面的值,节点随着改变就行,只是单向的数据流。

    9.数据和节点绑定

    因为dep数组里存的全是watcher对象,所以在dep发布变更通知的时候,会调用该watcher的update方法,来更新该watcher对应的节点值。

    watcher类该

    watcher原型里面的update方法,会在new Watcher()和dep.notify()的时候调用。so:数据监听和模板编译就是通过watcher连接起来的。

        各个函数的位置需要定位好,不然会出现xxx未定义或者xxx不是一个函数。可能我还有没讲明白的地方,我虽然明白,但是不知道怎么讲了,可以评论问我。

        最后附上我写的源码:https://github.com/qianluoluo/vueOrigin

    相关文章

      网友评论

        本文标题:Vue的双向数据绑定原理

        本文链接:https://www.haomeiwen.com/subject/tvvsoxtx.html