你真的理解Vue响应式吗

301 阅读3分钟

image.png

Vue2 响应式原理

vue2 的响应式也就是我们熟知的数据劫持,数据劫持到底是怎么实现的呢?

既然是劫持,肯定有一个源对象和一个劫持对象,那咱们先声明一个源对象 person 和 一个劫持对象 P

let person = {
    name: 'ikun',
    age: 18
};
let p = {};

vue2 响应式的原理就是通过 definedProperty 给 p 赋值,如果获取 p 的值则返回 person 的对应属性,如果是修改,则是将新的值赋值给 person

先对 person 开启劫持

Object.defineProperty(p, 'name', {


});

Object.defineProperty() 方法接收三个参数,第一个是 target 目标对象,第二个是 value 劫持属性,第三个是 description 描述,实现劫持,需要使用 decription 中的 set 和 get 方法


Object.defineProperty(p, 'name', {
    get() {
    
    },
    set(val) {
    
    }
    });
    
Object.defineProperty(p, 'age', {
    get() {
    
    },
    set(val) {
    
    }
});

当劫持对象 p 中的属性或者方法被枚举的时候会调用get,当 P 中的属性或者方法被修改的时候,会调用 set

let person = {
    name: 'ikun',
    age: 18
};
let p = {};
Object.defineProperty(p, 'name', {
    // 访问时会调用 getter
    get() {
        console.log('我被调用了')
        // 被调用的时候做些什么事        
        return person.name;
    },
    // 修改时会调用 setter
    set(val) {
        console.log('我被修改了')
        // 被修改的时候做些什么事        
        person.name = val;
    }
});
Object.defineProperty(p, 'age', {
    // 访问时会调用 getter
    get() {
        console.log('我被调用了')
        // 被调用的时候做些什么事
        return person.age
    },
    // 修改时会调用 setter
    set(val) {
        console.log('我被修改了')
        // 被修改的时候做些什么事                
        person.age = val
    }
});

set 方法有一个参数,就是被劫持对象的数据的新值,但是对于不存在或者不可枚举的属性无法劫持到,所以这就是为什么再 Vue2 中添加删除数组元素或者对象属性无法立即响应的原因

vue3 响应式原理

区别于 Vue2 的数据劫持 Object.defineProperty,Vue3使用的是html5的新api,Proxy 数据代理

Proxy 是一个构造函数,通过 new Proxy 可以实例化一个代理对象

我们还是先声明一个源对象

let person = {
        name: 'ikun',
        age: 18
};

然后将传入 new Proxy 的配置参数中

let p = new Proxy(person,{})

如果配置参数中无内容,则默认代理增删改查所有操作,即自动的对以上操作进行监听

配置参数中依然可以有 set 和 get,但是 set 方法可以对增添数据进行代理,但是无法监听到删除,需要使用 deleteProperty 对其代理

let p = new Proxy(person, {
    get(target, key) {
        
    },
    set(target, key, value) {
 
    },
    deleteProperty(target, key) {
    
    }
})

get 和 deleteProperty 方法中传入两个参数,第一个为 target 源对象,第二个是被枚举的属性的键名 key

set 方法中则多以参数,就是 value 修改的新值

所以对代理对象 p 的属性和方法进行修改和枚举的时候,我们可以对其进行一下操作

let p = new Proxy(person, {
    // get 传入两个参数,一个是源数据,一个是被读取的键名
    get(target, key) {
        console.log('p被读取了', a, b);
        // 使用 a[b] 既可以获取到对应的键名的值
        return a[b]
    },
    // set 方法接收三个参数,一个是源数据,一个是被被修改的键名,还有一个就是修改的值
    // 如果被设置了 set 属性,则不会再通过修改目标数据修改源数据
    set(target, key, value) {
        // 添加时也同样会调用 Set 方法
        console.log('p被修改了', target, key, value);
        person[key] = value
    },
    // 但是删除是无法捕获到的,所以需要使用另一个钩子 deleteProperty
    deleteProperty(target, key) {
        // deleteProperty 接收两个参数,一个是 target 源数据,一个是 key 被操作的键名
        console.log('p中的属性被删除了', target, key);
        // deleteProperty 方法存在有一个返回值,删除成功返回的是 true 否则是 false,删除关键字 delete 也有返回值,所以只需要将其返回即可
        return delete target[key]
    }
})

以上就是 vue2 和 vue3 的响应原理,希望能对您有所帮助