Vue2实现数据响应式

249 阅读2分钟

整理vue框架的原理解析知识点——响应式问题,我们知道vue框架是基于MVVM模式,所谓的MVVM模式如下图所示:

image.png

所谓的MVVM是指由把Controller的数据和逻辑处理部分从中抽离出来,用一个专门的对象去管理,这个对象就是ViewModel是Model和Controller之间的一座桥梁。当我们去尝试这种方式时,发现Controller中的代码变得非常少,变得易于测试和维护,只需要Controller和ViewModel做数据绑定即可,这也就我们常说的数据驱动视图

1. Vue2实现对象数据的响应式

我们知道对象数据实现响应式是基于Object.defineProperty()的get()和set()方法,代码如下:

    function defineReactive(obj, key, val) {
        get(){
            console.log('get '+ val)
            return val
        },
        set(newVal){
            console.log('set '+ key + ':'+ newVal)
            val = newVal
        }
    }

由此我们可以实现Object对象上属性的响应式,当我们设置初始值是,会调用get()方法,获取到当前的val值,但我们主动去修改值时,会触发set方法,完成响应式实现值的变更,对象上存在多个属性,那么如果我们每用一个值调用一次会比较麻烦,这个时候我们会通过遍历,来将Object上每个属性都附有响应式效果,代码如下:

//对象响应化,遍历每个key,定义getter,setter
function observe(obj) {
    if(typeof obj !== 'object' || obj == null){
        return
    }
    if(Array.isArray(obj)){
        //覆盖原型,替换7个变更操作
        obj.__protp__ = arrayProto
        //对数组内部的元素执行响应化
        const keys = Object.keys(obj)
        for(let i = 0;i <obj.length;i++){
            observe(obj[i])
        }
    }else{
        Object.keys(obj).forEach(key => {
            defineReactive(obj, key, obj[key])
        })
    }
    
}

如果Object中属性依然是Object该怎么办?那么这个时候我们就需要用到了递归这个方法,解决代码如下:

function defineReactive(obj, key, val){
        oberve(val)
        ///...
}

这里我们已经解决了对象数据的响应化处理,这个时候我们还遗漏了另外一种数据类型,就是数组(Array)了,这里我们提出了另外一种解决方案,将Array的原型prototype上的7个方法(push,pop,shift,unshift,splice,reverse,sort)增加通知更新的操作,代码如下:

//首先我们取出Array上的prototype
const originalProto = Array.prototype
//由于prototype具有链式关系,因此我们避免直接改动所有Array上的原型链方法,我们进行克隆备份
const arrayProto = Object.create(originalProto)
['push','pop','shift','unshift','splice','reverse','sort'].forEach(method => {
    arrayProto[method] = function() {
        //原始操作
        originalProto[method].apply(this, arguments)
        //通知更新
        set(){
            //...
        }
    }
})

数组原有方法的基础上,增加了数据监听依赖,以达到数组变化时候的 响应式更新,这样我们基本上成功实现了vue的响应式处理