现代前端指南!

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

2020-01-16  本文已影响0人  冰雪_666

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

1.Vue三要素
2.实现mvvm的双向绑定

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

接下来分别用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.区别

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)

三、总结

上一篇下一篇

猜你喜欢

热点阅读