vue3.0响应式原理简介

201 阅读2分钟

在Vue2.X中,是利用Object.defineProperty()方法来侦测对象的变化。但这种方法有以下几种缺陷
1) 无法侦测删除属性或新增属性
2) 改变数组的length属性无法被侦测
3) 性能较差

而Vue3.0中则使用了es6的Proxy来进行变化侦测。Proxy相比于Object.defineProperty()方法来讲性能更优异。数组的变化也可以直接get()和set()方法,不需要像Vue2.X那样单独处理数组的变化侦测。Proxy称为代理,他为javascript提供了元编程的能力,是一种可以直接拦截并改变底层javascript引擎操作的包装器。

对es6不太了解的朋友推荐阅读阮一峰的es6标准入门

下面是一个创建代理的简单示例

const target = {name: 'foo'}
const proxy = new Proxy(target, {
    //陷阱函数,读取target属性值时触发
    //三个参数分别是目标对象,读取的属性名,proxy对象本身
    get(target, property, receiver) {
        console.log(`${property}属性已被读取`)
        return target[property]
    },
    //改变属性值时触发
    set(target, property, value, receiver) {
        console.log(`${property}属性已被设置为${value}`)
        target[property] = value
        return value
    },
     //删除属性时触发
    deleteProperty (target, property) {
        console.log(`${property}属性已被删除`)
    }
})
const name = proxy.name //name属性已被读取
proxy.name = 'bar' //name属性已被设置为bar
delete proxy.name //name属性已被删除

上面是使用Proxy侦测对象变化的简单示例,下面让我们来模拟实现一下Vue3.0的响应式系统

//判断某个变量是否是对象的辅助函数
function isObject (val) {
    return val !== null && typeof val === 'object'
}

//创建响应式对象
function reactive (target) {
    return createReactiveObject(target)
}

function createReactiveObject (target) {
    //如果不是对象则直接返回
    if (!isObject(target)) {
        return target
    }
    const baseHandler = {
        get (target, property, receiver) {
            console.log('读取值')
            const result = Reflect.get(target, property, receiver)
            //递归地解析嵌套对象,解决多层对象嵌套问题
            return isObject(result) ? reactive(result) : result
        },
        set (target, property, value, receiver) {
            console.log('设置值')
            const result = Reflect.set(target, property, value, receiver)
            return result
        },
        deleteProperty (target, property) {
            console.log('删除值')
            const result = Reflect.deleteProperty(target, property)
            return result
        }
    }
    const observed = new Proxy(target, baseHandler)
    return observed
}

const proxy = reactive({name: 'foo'})
proxy.name = 'bar' //设置值
console.log(proxy.name)//读取值,bar

看过之前的例子,上面的代码应该很容易理解。在创建响应式对象的reactive函数中,我们只需要对传入的target对象进行一次包装,返回包装后的Proxy对象就可以。之后我们就可以在Proxy对象的陷阱函数里面侦测变化。