前言
本文旨在探讨 Vue 响应式系统的作用与实现方式,特别是响应式数据与副作用函数的概念及其在Vue 中的运用。
我们将分析如何通过拦截对象属性的读取和设置操作来实现响应式数据,并探讨 Vue2 与 Vue3 在实现响应式系统上的差异。
响应式数据与副作用函数
响应式数据是指当数据发生变化时,能够自动更新依赖于这些数据的视图或其他计算。
副作用函数是指执行时会直接或间接影响其他函数的执行,例如修改全局变量的函数。
function effect() {
document.body.innerHTML = obj.text;
}
在上述代码中,effect函数就是一个副作用函数,因为它直接修改了document.body.innerHTML。
响应式数据的基本实现
要实现响应式数据,关键在于拦截对象属性的读取和设置操作。
当副作用函数执行时,它会触发字段的读取操作;
当修改字段的值时,会触发字段的设置操作。
通过这种方式,我们可以在数据变化时重新执行相关的副作用函数。
1、 设置桶:创建一个桶来存放与响应式数据相关的副作用函数。 2、 读取数据:读取数据时,将副作用函数放入桶中。 3、 设置数据:修改数据时,执行桶中的所有函数。
在 Vue2 中,使用Object.defineProperty实现属性的拦截;
而在 Vue3 中,使用Proxy对象实现更彻底的拦截。
使用Proxy实现响应式数据
以下是使用Proxy实现响应式数据的简单示例:
const bucket = new Set(); // 存储副作用函数的桶
const data = { text: "hello world" }; // 原始数据
const obj = new Proxy(data, {
get(target, key) {
bucket.add(effect); // 将副作用函数effect添加到桶中
return target[key];
},
set(target, key, newVal) {
target[key] = newVal;
bucket.forEach((fn) => fn()); // 执行桶中的所有函数
return true;
},
});
function effect() {
document.body.innerHTML = obj.text;
}
effect(); // 执行副作用函数,触发读取
setTimeout(() => {
obj.text = "hello vue3"; // 1秒后修改响应式数据
}, 1000);
缺陷与改进
上述实现存在一个缺陷:硬编码了副作用函数的名字effect,导致如果函数名不叫effect,则代码无法工作。
为了解决这个问题,我们需要提供一个注册副作用函数的机制。
let activeEffect; // 用一个全局变量存储被注册的副作用函数
function effect(fn) {
activeEffect = fn;
fn();
}
通过这种方式,即使副作用函数是一个匿名函数,也能够被正确地收集到“桶”中,从而使响应系统不依赖于副作用函数的名字。
总结
Vue 的响应式系统通过拦截对象属性的读取和设置操作来实现数据的响应式更新。
Vue2 使用Object.defineProperty,而Vue3使用Proxy。
通过这种方式,我们可以确保当响应式数据发生变化时,相关的副作用函数能够被重新执行。
此外,通过提供一个注册副作用函数的机制,我们可以确保即使副作用函数是匿名的,也能够被正确地收集和执行,从而提高了响应式系统的灵活性和通用性。
– 欢迎点赞、关注、转发、收藏【我码玄黄】,各大平台同名。