vue响应式原理
vue2的响应式原理
vue2对对象类型的监听是通过Object.defineProperty实现的,给想要实现响应式的数据对象每个属性加上get,set方法,以实现数据劫持的操作。而对数组类型的监听是通过重写数组的方法实现的。
Object.defineProperty的定义见这里:Object.defineProperty
模拟vue2响应式实现
// 源数据
const person = {
name: "张三",
age: 18,
};
let p = {};
Object.defineProperty(p, "name", {
configurable: true,
get() {
console.log("获取了name");
return person.name;
},
set(name) {
console.log("修改了name"); // 模拟复杂的视图变动的代码
person.name = name;
},
});
Object.defineProperty(p, "age", {
configurable: true,
get() {
console.log("获取了age");
return person.age;
},
set(age) {
console.log("修改了age");
person.age = age;
},
});
调用结果:
存在问题:
- 对象类型不能监听到新增/删除属性的变动
- 数组类型不能监听到直接通过下标修改的变动
addSex/deleteName/deleteName点击没反应,但实际数据有修改
针对以上问题,vue2也提出一些api处理:
- this.$set/Vue.set:修改/新增属性的监听
- $delete/Vue.delete:删除属性的监听
所以,虽然vue2在响应式数据有些问题,但也提出了解决方案,并不是一无是处。
vue3的响应式原理
在说明vue3的响应式原理前先了解window的两个内置对象
链接在这:
Proxy :Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。
Reflect:Reflect 是一个内置的对象,它提供拦截 JavaScript 操作的方法。
Proxy
vue3是通过Proxy来实现响应式的,大致代码如下:
// 源数据
const person = {
name: "张三",
age: 18,
hobby: ["吃饭", "睡觉", "打豆豆"],
};
const p = new Proxy(person, {
get(target, property, receiver) {
console.log(`获取了${target}的${property}`);
return target[property];
},
set(target, property, value, receiver) {
console.log(`修改了${target}的${property}为${value}`);
target[property] = value;
},
deleteProperty(target, property, receiver) {
return delete target[property];
},
});
target是源对象, property是属性, receiver是代理对象
实际上重写了Proxy的get/set/deleteProperty方法,实现对对象属性的增删改查
Reflect
ECMA组织正在把Object上的一些象defineProperty()之类的方法有用的方法往Reflect上迁移,vue3响应式实现也用到Reflect
把上面的代码改造一下:
const p = new Proxy(person, {
get(target, property, receiver) {
console.log(`获取了${target}的${property}`);
return Reflect.get(target, property, receiver);
},
set(target, property, value, receiver) {
console.log(`修改了${target}的${property}为${value}`);
Reflect.set(target, property, value, receiver);
},
deleteProperty(target, property) {
return Reflect.deleteProperty(target, property);
},
});