Vue3 中的数据侦测是通过 Proxy 实现的。当我们创建一个响应式对象时,Vue3 内部会使用 Proxy 将其转化成一个响应式对象。当我们访问对象中的属性时,Vue3 会自动追踪这个属性,并将这个属性添加到依赖列表中。当响应式对象中的属性发生改变时,Vue3 就会更新视图。
下面我们来看具体的实现步骤:
使用 createReactiveObject 创建响应式对象
Vue3 使用 createReactiveObject 来创建响应式对象。createReactiveObject 函数接受 3 个参数:target、proxy、baseHandlers。其中,target 是我们要转化为响应式的对象,proxy 是一个 Proxy 实例,baseHandlers 是一个包含 get 和 set 函数的对象。
const target = {};
const proxy = new Proxy(target, baseHandlers);
const baseHandlers = {
get: function(target, prop) {
// get方法实现省略...
},
set: function(target, prop, value) {
// set方法实现省略...
}
};
const reactiveObj = createReactiveObject(target, proxy, baseHandlers);
实现 get 和 set 方法
我们需要在 baseHandlers 中实现 get 和 set 方法来拦截对象的读取和设置操作。
针对访问操作,get 方法应该执行以下操作:
- 获取属性值并返回。
- 如果当前属性只能读取而无法修改,则不将其添加到依赖列表中。
const baseHandlers = {
get: function(target, prop, receiver) {
let value = Reflect.get(target, prop, receiver);
// 将当前属性添加到依赖列表中
track(target, 'get', prop);
return value;
},
set: function(target, prop, value, receiver) {
let oldValue = Reflect.get(target, prop, receiver);
let res = Reflect.set(target, prop, value, receiver);
// 将当前属性对应的依赖列表中的所有 effect 都执行一遍
trigger(target, prop, value, oldValue);
return res;
}
};
针对修改操作,set 方法应该执行以下操作:
- 设置属性值。
- 将当前属性对应的依赖列表中的所有 effect 都执行一遍。
追踪依赖项
我们需要在 track 函数中实现依赖追踪功能。track 函数接受 3 个参数:target、type、key。其中,target 是我们要追踪的对象,type 是追踪类型(取值可以为 'get' 和 'set'),key 是我们追踪的属性名称。
let activeEffect = null;
const targetMap = new WeakMap();
function track(target, type, key) {
// 如果不存在活跃的 effect,则直接返回
if (!activeEffect) {
return;
}
let depsMap = targetMap.get(target);
// 如果当前对象还没有依赖列表,则创建一个新的依赖列表
if (!depsMap) {
depsMap = new Map();
targetMap.set(target, depsMap);
}
let dep = depsMap.get(key);
// 如果当前属性还没有依赖列表,则创建一个新的依赖列表
if (!dep) {
dep = new Set();
depsMap.set(key, dep);
}
// 把当前活跃的 effect 添加到依赖列表中
dep.add(activeEffect);
}
触发更新
我们需要在 trigger 函数中实现更新操作。trigger 函数接收 4 个参数:target、key、newValue、oldValue。其中,target 是我们要更新的对象,key 是我们要更新的属性名称,newValue 是更新后的新值,oldValue 是更新前的旧值。
function trigger(target, key, newValue, oldValue) {
let depsMap = targetMap.get(target);
if (!depsMap) {
// 当前对象没有依赖列表,说明没有 effect 依赖当前对象,则直接返回
return;
}
let dep = depsMap.get(key);
if (dep) {
// 如果当前属性对应的依赖列表中存在 effect,则执行这些 effect
dep.forEach(effect => {
effect(target, key, newValue, oldValue);
});
}
}
以上是 Vue3 中数据侦测的实现过程,下面给出具体的示例代码。
示例一
在这个示例中,我们创建一个响应式的对象 obj,当我们读取 obj 的属性时,它会自动添加到依赖列表中。
let target = { a: 1 };
let reactiveObj = reactive(target);
effect(() => {
console.log(reactiveObj.a);
});
// 控制台输出:1
示例二
在这个示例中,我们创建一个响应式的对象 obj,然后我们通过改变 obj 中的值,触发依赖列表中的函数执行。
let target = { a: 1 };
let reactiveObj = reactive(target);
effect(() => {
console.log(reactiveObj.a);
});
reactiveObj.a = 2; // 控制台输出:2
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Vue3 中的数据侦测的实现 - Python技术站