reactive:
一、这段是reactive方法的入口函数
export function reactive(target) { return createReactiveObject(target, reactiveMap, mutableHandlers);}
二、createReactiveObject是reactive真正调用的方法,在这个方法中核心调用的是proxy。
第一步、如果缓存中(proxyMap)存在target,就会从缓存中(proxyMap)返回target对应的值。
// 在这里是命名了一个 WeakMap 对象,WeakMap 对象是一组键/值对的集合,其中的键是弱引用的。其键必须是对象,而值可以是任意的。具体与 Map 对象的去区别可参考 MDNexport 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(); } }}