zoukankan      html  css  js  c++  java
  • Vue双向绑定原理及实现

    1. 前言

    • 2. 思路分析
    • 3. 使数据对象变得“可观测”
    • 4. 依赖收集
    • 5. 订阅者Watcher
    • 6. 测试
    • 7. 总结

      

    1. 前言

    每当被问到Vue数据双向绑定原理的时候,大家可能都会脱口而出:Vue内部通过Object.defineProperty方法属性拦截的方式,把data对象里每个数据的读写转化成getter/setter,当数据变化时通知视图更新。虽然一句话把大概原理概括了,但是其内部的实现方式还是值得深究的,本文就以通俗易懂的方式剖析Vue内部双向绑定原理的实现过程。

    2. 思路分析

    所谓MVVM数据双向绑定,即主要是:数据变化更新视图,视图变化更新数据。如下图:

    也就是说:

    • 输入框内容变化时,data 中的数据同步变化。即 view => model 的变化。
    • data 中的数据变化时,文本节点的内容同步变化。即 model => view 的变化。

    要实现这两个过程,关键点在于数据变化如何更新视图,因为视图变化更新数据我们可以通过事件监听的方式来实现。所以我们着重讨论数据变化如何更新视图。

    数据变化更新视图的关键点则在于我们如何知道数据发生了变化,只要知道数据在什么时候变了,那么问题就变得迎刃而解,我们只需在数据变化的时候去通知视图更新即可。

    3. 使数据对象变得“可观测”

    数据的每次读和写能够被我们看的见,即我们能够知道数据什么时候被读取了或数据什么时候被改写了,我们将其称为数据变的‘可观测’。

    要将数据变的‘可观测’,我们就要借助前言中提到的Object.defineProperty方法了,关于该方法,MDN上是这么介绍的:

    Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象。

    在本文中,我们就使用这个方法使数据变得“可观测”。

    首先,我们定义一个数据对象car

    let car = {
            'brand':'BMW',
            'price':3000
        }

    我们定义了这个car的品牌brandBMW,价格price是3000。现在我们可以通过car.brandcar.price直接读写这个car对应的属性值。但是,当这个car的属性被读取或修改时,我们并不知情。那么应该如何做才能够让car主动告诉我们,它的属性被修改了呢?

    接下来,我们使用Object.defineProperty()改写上面的例子:

      let car = {}
        let val = 3000
        Object.defineProperty(car, 'price', {
            get(){
                console.log('price属性被读取了')
                return val
            },
            set(newVal){
                console.log('price属性被修改了')
                val = newVal
            }
        })
    

      通过Object.defineProperty()方法给car定义了一个price属性,并把这个属性的读和写分别使用get()set()进行拦截,每当该属性进行读或写操作的时候就会出发get()set()。如下图:

    可以看到,car已经可以主动告诉我们它的属性的读写情况了,这也意味着,这个car的数据对象已经是“可观测”的了。

    为了把car的所有属性都变得可观测,我们可以编写如下两个函数:

    /**
         * 把一个对象的每一项都转化成可观测对象
         * @param { Object } obj 对象
         */
        function observable (obj) {
            if (!obj || typeof obj !== 'object') {
                return;
            }
            let keys = Object.keys(obj);
            keys.forEach((key) =>{
                defineReactive(obj,key,obj[key])
            })
            return obj;
        }
        /**
         * 使一个对象转化成可观测对象
         * @param { Object } obj 对象
         * @param { String } key 对象的key
         * @param { Any } val 对象的某个key的值
         */
        function defineReactive (obj,key,val) {
            Object.defineProperty(obj, key, {
                get(){
                    console.log(`${key}属性被读取了`);
                    return val;
                },
                set(newVal){
                    console.log(`${key}属性被修改了`);
                    val = newVal;
                }
            })
        }
    

      现在,我们就可以这样定义car:

    let car = observable({
            'brand':'BMW',
            'price':3000
        })
    

      car的两个属性都变得可观测了。


    完整代码,请戳这里☞ vue数据双向绑定原理及实现

    vue数据双向绑定是通过数据劫持Object.defineProperty( )结合发布者-订阅者模式的方式来实现的

    实现过程:
    1、首先要对数据进行劫持监听,所以我们需要设置一个监听器Observer,用来监听所有属性。
    2、如果属性发上变化了,就需要告诉订阅者Watcher看是否需要更新。
    3、因为订阅者是有很多个,所以我们需要有一个消息订阅器Dep来专门收集这些订阅者,然后在监听器Observer和订阅者Watcher之间进行统一管理的。
    4、我们还需要有一个指令解析器Compile,对每个节点元素进行扫描和解析,将相关指令对应初始化成一个订阅者Watcher,并替换模板数据或者绑定相应的函数,此时当订阅者Watcher接收到相应属性的变化,就会执行对应的更新函数,从而更新视图。



    7. 总结

    总结一下:

    实现数据的双向绑定,首先要对数据进行劫持监听,所以我们需要设置一个监听器Observer,用来监听所有属性。如果属性发上变化了,就需要告诉订阅者Watcher看是否需要更新。因为订阅者是有很多个,所以我们需要有一个消息订阅器Dep来专门收集这些订阅者,然后在监听器Observer和订阅者Watcher之间进行统一管理的。

    实用特此记录-

    本文地址 https://www.cnblogs.com/wangjiachen666/p/9883916.html#_labelTop

    爱生活、爱编程!
  • 相关阅读:
    SPI
    CAP、BASEd、二阶段提交协议、三阶段提交协议、拜占庭将军问题、paxos、Raft、ZAB、NWR
    分布式理论笔记
    springboot自动配置原理
    docker网络
    Nginx主从配置、Keepalived、VRRP协议
    Nginx
    spring cloud
    Dockerfile、push到阿里云
    镜像、容器、部署tomcat、修改容器为新镜像、容器卷
  • 原文地址:https://www.cnblogs.com/liliuyu/p/11749010.html
Copyright © 2011-2022 走看看