Contents

vue2和vue3的响应式区别

vue2响应式

vue2响应式的核心是通过defineProperty来对对象已有的属性值的读取和修改进行劫持(监视/拦截)

对象的响应式实现

// 简单实现
// 假设app为vue实例
const app = {}
const data = {
    name: 'zs',
    age: 18
}
// 遍历data,将data属性绑定到app上,对属性的读取和修改进行拦截
Object.entries(data).forEach(([prop, value]) => {
    let initValue = value;
    Object.defineProperty(app, prop, {
        get() {
            console.log('defineProperty内部监视  执行get')
            return initValue
        },
        set(newValue) {
            console.log('defineProperty内部监视  执行set');
            initValue = newValue
        }
    })
})

console.log(app.name); // defineProperty内部监视  执行get zs
app.name = 'ls'; // defineProperty内部监视  执行set
app.sex = '男'; // 不会执行set方法
console.log(app.sex); // 不会执行get方法

数组的响应式实现

通过重写数组的一系列更新元素方法来实现元素的修改劫持

// 简单实现
const obj = {
    push() {},
    pop() {},
    shift() {},
    unshift() {},
    splice() {},
    sort() {},
    reverse() {}
}
Object.keys(obj).forEach(key => {
    Object.defineProperty(obj, key, {
        value(...args) {
            console.log('args', args);
            return Array.prototype[key].call(this, ...args)
        }
    })
})

const arr = [];
arr.__proto__ = obj;
arr.push(1, 2); // args[1,2]
console.log(arr[0]); // 1

我们知道arr.__proto__等于它的构造函数的原型,也就是Array.prototype,所以arr可以执行push,pop等方法,但是现在arr.__proto__指向obj,所以arr.push相当于obj.pushdefineProperty进行监听,obj.push()就会执行value函数,通过call方法进行数组操作

vue3响应式

通过Proxy来拦截对对象本身的操作,包括属性的读写,属性的添加,属性的删除等… 通过Reflect来动态对被代理的对象的相应属性进行特定操作

// 简单实现:
const user = {
    name: 'zs',
    age: 18
}
const proxy = new Proxy(user, {
    get(target, prop) {
        console.log('劫持get操作', prop)
        return Reflect.get(target, prop)
    },
    set(target, prop, val) {
        console.log('劫持set()', prop, val);
        return Reflect.set(target, prop, val)
    },
    deleteProperty(target, prop) {
        console.log('劫持delete', prop);
        return Reflect.deleteProperty(target, prop)
    },
})

// 读取属性值
console.log(proxy === user);
console.log(proxy.name); // 劫持get() name zs
// 设置属性值
proxy.name = 'ls'; // 劫持set() name ls
proxy.age = 19; // 劫持set() age 19
console.log(user); // { name: 'ls', age: 19 }
// 添加属性
proxy.sex = '男'; // 劫持set() sex 男
console.log(user); //{ name: 'ls', age: 19, sex: '男' }
// 删除属性
delete proxy.sex; // 劫持delete sex
console.log(user); // { name: 'ls', age: 19 }

总结:正是由于vue3使用proxy代理的方式拦截对象本身,所以在vue3中添加/删除属性都是响应式的,通过下标修改数组也是响应式