Vue实现双向数据绑定Proxy和defineproperty区别

一、实现mvvm的双向绑定原理

1.Vue三要素
  • 响应式: 例如如何监听数据变化,其中的实现方法就是我们提到的双向绑定
  • 模板引擎: 如何解析模板
  • 渲染: Vue如何将监听到的数据变化和解析后的HTML进行渲染
2.实现mvvm的双向绑定

是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()或者vue3.0的Proxy来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。就必须要实现以下几点:

  • 实现一个数据监听器Observer,能够对数据对象的所有属性进行监听,如有变动可拿到最新值并通知订阅者
  • 实现一个指令解析器Compile,对每个元素节点的指令进行扫描和解析,根据指令模板替换数据,以及绑定相应的更新函数
  • 实现一个订阅者Watcher,作为连接Observer和Compile的桥梁,能够订阅并收到每个属性变动的通知,执行指令绑定的相应回调函数,从而更新视图
  • mvvm入口函数,整合以上三者


    Vue实现双向数据绑定Proxy和defineproperty区别_第1张图片
    1.png

接下来分别用Proxy和defineproperty实现一个简单的数据劫持,没有指令解析器Compile和订阅者Watcher

二、Proxy和defineproperty区别

1.defineproperty

1.1.区别

  • 无法监听数组变化
  • 只能劫持对象的属性,因此我们需要对每个对象的每个属性进行遍历,如果属性值也是对象那么需要深度遍历
  • 兼容性好

1.2.实现
1.2.1.定义Vue对象

function Vue(data,el){
    this.$data=data
    this.el=el;
    this.virtualdom=""
    this.arrPro=Array.prototype;
    this.arrayObj=Object.create(this.arrPro)
    this.HandlerArray()
    this.observe(this.$data)
}

1.2.2.利用装饰者设计模式装饰数组,实现操作数组视图更新

Vue.prototype.HandlerArray=function(){
    var self=this
    var arrMethods=['push','pop','shift','unshift','splice','sort','reverse'];
    arrMethods.forEach((val)=>{//装饰者模式
        self.arrayObj[val]=function(){
            let ret=self.arrPro[val].apply(this,arguments);
            self.render(arguments[0])
            return ret
        }
    })
}

1.2.3.观察者模式,包括对数组和对象的监听。此处省略依赖收集和触发依赖

Vue.prototype.observe=function(obj){
    var self=this
    if(!obj) return
    if(obj instanceof Array){
        obj.__proto__=self.arrProObj
        return
    }
    for(var key in obj){
        var value=obj[key]
        if(value instanceof Array){
            value.__proto__=self.arrayObj
            return
        }
        if(typeof value=='object'){
            self.observe(value)
            return 
        }
        Object.defineProperty(obj,key,{
            get:function(){
                //省略依赖收集
                return value
            },
            set:function(newVal){
                //省略触发依赖
                value=newVal
                self.render(newVal)//代替notify
            }
        })
        
    }
}

1.2.4.生成视图。此处省略读取视图模板,生成ast语法树,diff对比

//省略读取视图模板,生成ast语法树,diff对比
Vue.prototype.render=function(value){
    this.virtualdom="i am is "+value
    this.el.innerHTML=this.virtualdom
}

1.2.5.调用示例

var vue= new Vue({
        a:'1',
        b:[1,2,3]
    },document.getElementById('app'))

vue.$data.a=444
setTimeout(()=>{
    vue.$data.a=999
},1000)
2.Proxy

2.1.区别

  • 可以直接监听对象而非属性,所以不必遍历监听属性
  • Proxy可以直接监听数组的变化
  • 作为新标准将受到浏览器厂商重点持续的性能优化,也就是传说中的新标准的性能红利
  • 不兼容IE,是 ES6 为了操作对象而提供的新 API

2.2.具体实现
2.2.1.定义Vue对象

function Vue(data,el){
    this.$data=data
    this.el=el
    this.virtualdom=""
    this.observe(this.$data)
}

2.2.2.观察者模式,即可以监听数据也可以监听对象。此处省略依赖收集和触发依赖。此处用到了reflect,可自行查阅http://es6.ruanyifeng.com/#docs/reflect

Vue.prototype.observe=function(obj){
    this.$data=this.proxy(obj)
}
Vue.prototype.proxy=function(obj){
    var self=this
    var proxy=null
    proxy = new Proxy(obj,{
        //省略依赖收集
        get:function(target,propkey){
            return Reflect.get(target,propkey)
        },
        //省略触发依赖
        set:function(target,propkey,value){
            if(propkey!='length' && typeof value != 'object'){
                self.render(value)//代替notify
            }
            return Reflect.set(target,propkey,value)
        }
    })
    //递归监听对象
    for(var index in proxy){
        if(typeof proxy[index] =='object'){
            proxy[index]=this.proxy(proxy[index])
        }
    }
    return proxy
}

2.2.3.生成视图。此处省略读取视图模板,生成ast语法树,diff对比

//省略读取视图模板,生成ast语法树,diff对比
Vue.prototype.render=function(value){
    this.virtualdom="i am is "+value
    var pEl=document.createElement('p')
    pEl.innerHTML=this.virtualdom
    this.el.appendChild(pEl)
}

2.2.4.具体调用

var vue= new Vue([1,4,5],document.getElementById('app'))
vue.$data.push('444')

setTimeout(function(){
    vue.$data.push('666')
},1000)

三、总结

  • 从代码数量和性能上看Proxy要好很多
  • 如果有兴趣朋友想了解双向数据绑定具体代码的实现,可去https://www.cnblogs.com/canfoo/p/6891868.html进行了解,个人感觉总结不错。
  • 还请各位大佬指出不足之处

你可能感兴趣的:(Vue实现双向数据绑定Proxy和defineproperty区别)