一、数据响应是什么?
“响应式”,是指当数据改变后,Vue 会通知到使用该数据的代码。例如,视图渲染中使用了数据,数据改变后,视图也会自动更新。
二、Vue2中如何实现的呢?
在Vue2中使用的是Object.defineProperty
实现的数据劫持响应。
/**
* 使用 Object.defineProperty 测试响应式
*/
(function () {
const data = {
name: "admin",
age: 10,
info: {
address: "上海" // 深度监听
},
friends: ["西奥", '(*^_^*)', '吃饭'] // 监听数据的测试数据
}
Object.defineProperty
// 开启对数组的监听
// 复制数组的原型链,这里是为了不污染原有的原型链
const sourceArrayProto = Array.prototype;
const customArrayProto = Object.create(sourceArrayProto);
// 重写数组的基本方法
['push', 'shift', 'pop', 'unshift', 'splice'].forEach(methodName => {
customArrayProto[methodName] = function () {
// 修改原数组的数据,这里需要请求源数据原型链的支持
sourceArrayProto[methodName].call(this, ...arguments);
// 通知更新
updateView();
}
})
// 更新视图
function updateView() {
console.log("update data");
}
// 重新定义属性,监听对象的每一个属性
function defineReactive(target, key, value) {
Object.defineProperty(target, key, {
get() {
// fix 开启深度监听
observer(value);
return value;
},
set(newVal) {
if (value !== newVal) {
// fix 开启深度监听
observer(value);
// 注意,value一直都是闭包,此处设置完成后,在get到value
value = newVal;
updateView();
}
}
})
}
function observer(target) {
// 不是对象或数组
if (typeof target !== "object" || target === null) {
return target;
}
// 针对数组特殊处理,因为 Object.defineProperty 无法监听数组
if (Array.isArray(target)) {
// 修改成自定义的原型链,达到监听数组变化的目的
target.__proto__ = customArrayProto;
return;
}
// 重新定义各个属性
for (let key in target) {
defineReactive(target, key, target[key]);
}
}
// 监听数据的响应式
observer(data);
// 修改数据
// 简单对象
// data.name = "jack";
// data.age = 52;
// 深度监听
// data.info.address = "上海市"
// 修改数组数据,完成数组的监听
data.friends.push("卡哇伊");
console.log(data);
})();
三、Vue3中如何实现的呢?
在Vue3中使用的是Proxy
实现的代理数据响应。
(function () {
const data = {
name: "admin",
age: 10,
info: {
address: "上海" // 深度监听
},
friends: ["西奥", '(*^_^*)', '吃饭'] // 监听数据的测试数据
}
function reactive(target) {
// 不是对象或数组,则返回
if (typeof target !== "object" || target === null) {
return target;
}
const proxyConfig = {
get(target, key, receiver) {
const result = Reflect.get(target, key, receiver);
console.log('get', key);
console.log('result', result);
// 深度监听
return reactive(result);
},
set(target, key, value, receiver) {
// 重复数据,不处理,提高性能
if (value === target[key]) {
return true;
}
const result = Reflect.set(target, key, value, receiver);
console.log('set', key, value);
console.log('result', result);
return result;
},
deleteProperty(target, key) {
const result = Reflect.deleteProperty(target, key);
console.log('delete', key);
console.log('result', result);
return result;
}
}
const observed = new Proxy(target, proxyConfig);
return observed;
}
const proxyData = reactive(data);
// 修改数据
// 简单对象
// proxyData.name = "jack";
// proxyData.age = 52;
// 深度监听
proxyData.info.address = "上海市"
// 修改数组数据,完成数组的监听
// proxyData.friends.push("卡哇伊");
// console.log(proxyData.name);
console.log(proxyData);
})();
总结
无论是Vue2的Object.defineProperty
,还是Vue3的Proxy
,皆有自己的优点和缺点
Vue2的优点就是胜在兼容性强,能兼容ie。
至于缺点也不少,使用Object.defineProperty
进行监听数据变化,如果需要深度监听,需要递归到底,一次性计算量大,并且无法监听新增属性/删除属性,这也是为什么Vue会提供Vue.delete
、Vue.set
的原因。而且使用Object.defineProperty
还有一个致命的缺点,它无法原生监听数组,需要特殊处理,这个可以参考上面的案列。
Vue3最大的优点就是Proxy
能规避Object.defineProperty
的问题,它能监听数组变化、新增/删除 属性,也可以深度监听,性能更好(什么时候用到,什么时候监听)
但是Vue3的Proxy
也有一个致命的缺点,完全放弃对ie的兼容性,这也是为什么Vue3又推出了一个版本,针对ie的兼容版(使用Object.defineProperty
)
原创不易,转载请标名出处