## vue2中的数据响应式原理

35 阅读1分钟

除了理解该原理外,日后可用于需要用不同方式处理对象、数组及基本类型数据的场景

vue对于数据的响应式是直接调用observe(data),将option中的data传入

export function observe(data){
    // 后续递归响应object类型数据(包括array)
    if(typeof data !== 'object' || data === null){
        return
    }
    return new Observe(data)
}

接下来看一下Observe这个类

Observe

在上一步中已经将数据进行过滤,所以这里进来分别处理对象和数组

walk、arrayObserve

class Observe{
    constructor(data){
        if(Array.isArray(data)){
            this.arrayObserve(data)
        }else{
            this.walk(data)

        }
    }
    walk(data){
        for(let key in data){
            defineReactive(data,key,data[key])
        }
    }
    arrayObserve(value){
        for(let i = 0;i < value.length;i++){
            observe(value[i])
        }
    }
}

由于数组中可能存在对象或数组,所以递归使用observe(对象中存在的相关情况会在defineReactive中处理)

接下来看下对object的处理函数defineReactive

function defineReactive(data,key,value){
        observe(value)
        Object.defineProperty(data,key,{
            get(){
                ...
                console.log('收集依赖')
                return value
            },
            set(newValue){
                observe(value)
                value = newValue
                ...
                console.log('更新操作')
            }
        })
    }

在处理object时,object的值也可能是object (例如father.wife = {name:'merry'}),因此需要递归调用observe处理

而在给某个赋值的时候,可能给的也是对象,因此需要在set中递归调用下observe

响应式的处理也主要是在get和set中进行

数组方法的处理

接下来还有一个问题,就是在调用数组方法push,shift,unshift...时,也需要进行响应式操作

这里需要改造一下Observe类,如下

...
constructor(data){
    		// 添加__ob__一个作用是标志为响应式对象,还有就是在更改数组操作时要用到
            Object.defineProperty(data,'__ob__',{
                configurable:false,
                enumerable:false,
                value:this
            })
            if(Array.isArray(data)){
                data.__proto__ = arrayMethods
                this.arrayObserve(data)
            }else{
                this.walk(data)

            }
        }
...

下面时arrayMethods的相关操作,主要是修改了相关数据的原型

先保留Array的原型到变量oldArrayMethods上,接着继承并暴露arrayMethods,

利用一个数组保存需要改造的methods,然后在后面的操作中对其进行响应式操作

let oldArrayMethods = Array.prototype
export let arrayMethods = Object.create(oldArrayMethods)
const methodsToPatch = [  'push',
  'pop',
  'shift',
  'unshift',
  'splice',
  'sort',
  'reverse']

利用原型链的屏蔽规则,给对象添加相关属性时,先调用原型链上的方法,以此保证程序的正常进行,并在最后返回结果

对于上述操作,当插入数据时需要对其进行响应式操作,插入的操作主要包括push,unshift和splice,这三者的插入元

素分别如下

push:第1个参数即为插入元素

unshift:第1个参数即为插入元素

splice:第3个参数即为插入元素

当检测到有插入元素时,调用observeArray进行更新,代码如下

methodsToPatch.forEach(method=>{
     arrayMethods[method] = function(...args){
        let result = oldArrayMethods[method]apply(this,args)
        let ob = this.__ob__
        let insert = null
        switch(method){
                case 'push':
                case 'unshift':
                	insert = args
                break;
                case 'splice':
                	insert = args.slice(2)
                break;
        }
        if(insert) ob.observeArray(insert)
        console.log('更新操作')
        return result 
    }
})