reactive:
一、这段是reactive方法的入口函数
export function reactive(target) {
return createReactiveObject(target, reactiveMap, mutableHandlers);
}
二、createReactiveObject是reactive真正调用的方法,在这个方法中核心调用的是proxy。
第一步、如果缓存中(proxyMap)存在target,就会从缓存中(proxyMap)返回target对应的值。
// 在这里是命名了一个 WeakMap 对象,WeakMap 对象是一组键/值对的集合,其中的键是弱引用的。其键必须是对象,而值可以是任意的。具体与 Map 对象的去区别可参考 MDN
export const reactiveMap = new WeakMap();
第二步、如果缓存中不存在 target,将命中 proxy 方法,将通过 baseHandlers 方法处理后的 target 存入 proxyMap 中。接下来分析一下 Proxy 方法是如何工作的。
定义: Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。
语法:const p = new Proxy(target, handler)
参数:
target: 要使用 Proxy 包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)。
handler: 一个通常以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时代理 p 的行为。
简单理解为,proxy 对象就是通过 handler 方法去操作 target ,最后返回被重新定义的 target 值。
function createReactiveObject(target, proxyMap, baseHandlers) {
// 核心就是 proxy
// 目的是可以侦听到用户 get 或者 set 的动作
// 如果命中的话就直接返回就好了
// 使用缓存做的优化点
const existingProxy = proxyMap.get(target);
if (existingProxy) {
return existingProxy;
}
const proxy = new Proxy(target, baseHandlers);
// 把创建好的 proxy 给存起来,
proxyMap.set(target, proxy);
return proxy;
}
三、接下来分析,源码中 Proxy 对象调用的 baseHandlers 方法。
// mutableHandlers 的入口函数,最终是调用了自定义的 get 和 set 方法
export const mutableHandlers = {
get,
set,
};
1、get 方法分析:
第一步、判断当前 get 方法是被 reactive 触发还是 Readonly
第二步、命中 Reflect.get() 方法,从 target 对象中 获取 key 对应的值,返回该值为 res
定义:**Reflect.get()**方法与从 对象 (target[propertyKey]) 中读取属性类似,但它是通过一个函数执行来操作的。
语法:Reflect.get(target, propertyKey[, receiver])
参数: target: 需要取值的目标对象
propertyKey: 需要获取的值的键值
receiver: 如果target对象中指定了getter,receiver则为getter调用时的this值。
简单理解该方法,从 target 对象中获取 propertyKey 对应的值,最后返回该值
第三步、如果是 reactive 触发的 get 方法,就触发 track 方法进行依赖收集(源码分析在下面)
第四步、判断 isReadonly,将 res 分别以 readonly(res) 和 reactive(res) 的形式包装返回
const get = createGetter();
export const enum ReactiveFlags {
IS_REACTIVE = "__v_isReactive",
IS_READONLY = "__v_isReadonly",
RAW = "__v_raw",
}
function createGetter(isReadonly = false, shallow = false) {
return function get(target, key, receiver) {
// 此处的三个方法,是在判断 key 是属于哪个方法,且 receiver 是否存在于缓存中
const isExistInReactiveMap = () =>
key === ReactiveFlags.RAW && receiver === reactiveMap.get(target);
const isExistInReadonlyMap = () =>
key === ReactiveFlags.RAW && receiver === readonlyMap.get(target);
const isExistInShallowReadonlyMap = () =>
key === ReactiveFlags.RAW && receiver === shallowReadonlyMap.get(target);
if (key === ReactiveFlags.IS_REACTIVE) {
return !isReadonly;
} else if (key === ReactiveFlags.IS_READONLY) {
return isReadonly;
} else if (
isExistInReactiveMap() ||
isExistInReadonlyMap() ||
isExistInShallowReadonlyMap()
) {
return target;
}
const res = Reflect.get(target, key, receiver);
// 问题:为什么是 readonly 的时候不做依赖收集呢
// readonly 的话,是不可以被 set 的, 那不可以被 set 就意味着不会触发 trigger
// 所有就没有收集依赖的必要了
if (!isReadonly) {
// 在触发 get 的时候进行依赖收集
track(target, "get", key);
}
if (shallow) {
return res;
}
if (isObject(res)) {
// 把内部所有的是 object 的值都用 reactive 包裹,变成响应式对象
// 如果说这个 res 值是一个对象的话,那么我们需要把获取到的 res 也转换成 reactive
// res 等于 target[key]
return isReadonly ? readonly(res) : reactive(res);
}
return res;
};
}
此处为第三步的源码分析:
export function track(target, type, key) {
if (!isTracking()) {
return;
}
console.log(`触发 track -> target: ${target} type:${type} key:${key}`);
// 1. 先基于 target 找到对应的 dep
// 如果是第一次的话,那么就需要初始化
let depsMap = targetMap.get(target);
if (!depsMap) {
// 初始化 depsMap 的逻辑
depsMap = new Map();
targetMap.set(target, depsMap);
}
let dep = depsMap.get(key);
if (!dep) {
dep = createDep();
depsMap.set(key, dep);
}
trackEffects(dep);
}
export function trackEffects(dep) {
// 用 dep 来存放所有的 effect
// TODO
// 这里是一个优化点
// 先看看这个依赖是不是已经收集了,
// 已经收集的话,那么就不需要在收集一次了
// 可能会影响 code path change 的情况
// 需要每次都 cleanupEffect
// shouldTrack = !dep.has(activeEffect!);
if (!dep.has(activeEffect)) {
dep.add(activeEffect);
(activeEffect as any).deps.push(dep);
}
}
2、set 方法分析
第一步、命中 Reflect.set 方法,向 target 对象中添加 value值,属性名称设置为 key
定义:Reflect.set 方法允许你在对象上设置属性。它的作用是给属性赋值并且就像 property accessor 语法一样,但是它是以函数的方式。
语法:Reflect.set(target, propertyKey, value[, receiver])
参数: target: 设置属性的目标对象。
propertyKey: 设置的属性的名称。
value: 设置的值。
receiver: 如果遇到 setter,receiver则为setter调用时的this值。
简单理解该方法,向 target 对象中添加 value值,属性名称设置为 propertyKey,最后返回一个 Boolean 值
第二步、命中 trigger 方法(在下方进行分析)
第三步、返回 result,这是一个 Boolean 值
const set = createSetter();
function createSetter() {
return function set(target, key, value, receiver) {
const result = Reflect.set(target, key, value, receiver);
// 在触发 set 的时候进行触发依赖
trigger(target, "set", key);
return result;
};
}
此处为第二步的源码补充分析:
export function trigger(target, type, key) {
// 1. 先收集所有的 dep 放到 deps 里面,
// 后面会统一处理
let deps: Array<any> = [];
// dep
const depsMap = targetMap.get(target);
if (!depsMap) return;
// 暂时只实现了 GET 类型
// get 类型只需要取出来就可以
const dep = depsMap.get(key);
// 最后收集到 deps 内
deps.push(dep);
const effects: Array<any> = [];
deps.forEach((dep) => {
// 这里解构 dep 得到的是 dep 内部存储的 effect
effects.push(...dep);
});
// 这里的目的是只有一个 dep ,这个dep 里面包含所有的 effect
// 这里的目前应该是为了 triggerEffects 这个函数的复用
triggerEffects(createDep(effects));
}
export function triggerEffects(dep) {
// 执行收集到的所有的 effect 的 run 方法
for (const effect of dep) {
if (effect.scheduler) {
// scheduler 可以让用户自己选择调用的时机
// 这样就可以灵活的控制调用了
// 在 runtime-core 中,就是使用了 scheduler 实现了在 next ticker 中调用的逻辑
effect.scheduler();
} else {
effect.run();
}
}
}
文章详细解析了Vue3中reactive函数的实现,通过Proxy对象创建目标对象的代理,实现数据的拦截和自定义行为。get方法中进行依赖收集,set方法触发依赖更新。createReactiveObject方法核心是使用Proxy,并利用WeakMap优化缓存。baseHandlers包含了get和set方法,get方法不仅返回值,还处理依赖收集,而set方法则在修改数据后触发依赖更新。
573

被折叠的 条评论
为什么被折叠?



