本文记录实现一个 mini-vue3 的关键点,用来辅助自己从 0 到 1 实现一个 mini-vue3。
dep
createDep(effects?):
- 作用:创建 dep
- 主要逻辑:
- 使用 Set 存储 ReactiveEffect 的实例对象
reactive
缓存
export const reactiveMap = new WeakMap();
export const readonlyMap = new WeakMap();
export const shallowReadonlyMap = new WeakMap();
reactive(target):
- 作用:转成响应式对象
- 参数:
- 主要逻辑:
- createReactiveObject(target, reactiveMap, mutableHandlers)
createReactiveObject(target, proxyMap, baseHandlers):
- 参数:
- proxyMap:缓存
- baseHandlers:操作器,查看 baseHandlers.md
- 主要逻辑:
- 判断是否有缓存,有缓存直接返回
- 使用 proxy
- 将返回的 proxy 对象加入缓存
readonly(target):
- createReactiveObject(target, readonlyMap, readonlyHandlers);
shallowReadonly(target):
- createReactiveObject(target, shallowReadonlyMap, shallowReadonlyHandlers);
isProxy(value):
- isReactive(value) || isReadonly(value);
isReactive(value):
- 主要逻辑:
- !!value[ReactiveFlags.IS_REACTIVE];
- 若 value 是 proxy,则会调用 get,转 baseHandlers 的 createGetter 函数返回布尔值
- 否则为 undefined,!! 转布尔值
isReadonly(value):
- !!value[ReactiveFlags.IS_READONLY];
toRaw(value):
- 主要逻辑:
- 若 value 是 proxy,转 baseHandlers 的 createGetter 函数直接返回
- 否则为 undefined 直接返回原 value
baseHandlers
createGetter(isReadonly: boolean, shallow: boolean)
- 作用:用于 proxy 中的 get 操作器
- 参数:
- isReadonly 是否是 readonly
- shallow 是否是 shallow
- 主要逻辑:
- 返回与 proxy 格式相同的 get 函数(即 get 操作器)
- 根据 key、receiver 判断,被用于 isReactive、isReadonly、isRaw 函数
- 若 key === ReactiveFlags.IS_REACTIVE,返回 !isReadonly
- 若 key === key === ReactiveFlags.IS_READONLY,返回 isReadonly
- 若 key === ReactiveFlags.RAW 并且存在某个缓存中,即存在响应式对象,直接返回 target
- 使用 Reflect.get 获取值
- 调用 track 函数进行依赖收集,注意 readonly 不需要
- shallow 直接返回结果
- 若返回值为对象,reactive 需要对其调用 reactive 再返回
createSetter()
- 作用:用于 proxy 中的 set 操作器
- 主要逻辑:
mutableHandlers()
- 作用:封装 reactive 的操作器,使用 createGetter()、createSetter()
readonlyHandlers()
- 作用:封装 readonly 的操作器,仅使用 createGetter(true)
shallowReadonlyHandlers()
- 作用:封装 shallowReadonly 的操作器,仅使用 createGetter(true, true)
effect
变量注解
- dep:为 Set,保存 ReactiveEffect 的实例
- effect:ReactiveEffect 的实例
effect(fn, options = {}):
- 作用:入口函数
- 参数:
- fn:依赖的函数
- options:配置,可传入 scheduler 或 onStop
- 主要逻辑:
- 创建 ReactiveEffect 实例
- 将 option 合并到实例中
- 使用实例执行 run 方法
- 实例的 run 方法绑定 this 为该实例并赋值给变量 runner(让用户可自行选择调用时机)
- 在 runner 对象上挂载 effect 方法指向该实例(是为了 triggerEffects 函数可以调用实例上的方法(比如 scheduler))
- 返回 runner
全局变量
- let activeEffect = void 0:用于获取执行函数的 ReactiveEffect 封装
- let shouldTrack = false
- const targetMap = new WeakMap()
class ReactiveEffect:
- 作用:其实例为依赖项的封装
- 值:
- active = true:是否需要收集依赖
- deps = []:依赖项对响应式对象的依赖
- onStop?: () => void:清除依赖的回调函数
constructor(public fn, public scheduler?):
- 参数:
- scheduler:回调函数。当触发依赖时,不调用 fn 而是调用 scheduler,逻辑在 isTracking 函数。computed 的实现逻辑就使用了该参数
run():
- 作用:进行依赖收集
- 主要逻辑:
- 判断 active
- 开启依赖收集:shouldTrack = true、activeEffect = this
- 重置,关闭收集依赖
stop():
- 作用:清除依赖项对响应式对象的依赖
- 主要逻辑:
- 若 active 为 true,执行 cleanupEffect、onStop
- active = false
cleanupEffect(effect):
- 作用:清除操作
- 主要逻辑:
- 删除 deps 中所有 set 的 effect
- deps.length = 0
track(target, type, key):
- 作用:查找 target 对应的 depsMap 对应的属性依赖项 dep,用于 get
- 主要逻辑:
- isTracking 函数判断
- 有相关的数据结构直接获取,没有则创建
- depsMap = targetMap.get(target)
- dep = depsMap.get(key)
- trackEffects(dep)
trackEffects(dep):
- 作用:收集依赖
- 主要逻辑:
- dep.add(activeEffect)
- activeEffect.deps.push(dep)
trigger(target, type, key):
- 作用:触发依赖,用于 set
- 主要逻辑:
- 获取 dep,同上
- 将 deps 的所有 dep 合并为一个 dep,传入 triggerEffects 函数
isTracking():
- shouldTrack && activeEffect !== undefined
triggerEffects(dep):
- 主要逻辑:
- 遍历 dep,若 effect 有 scheduler 方法则执行,否则执行 run 方法
ref
ref(value):
class RefImp:
- 变量:
- __rawValue:保存原始对象
- _value:保存 ref 对象
- dep
- __v_isRef:是否 ref
constructor(value):
- 作用:创建一个 ref 对象
- 主要逻辑:
- __value = convert(value)
- dep = createDep()
get value():
set value(newValue):
- 主要逻辑:
- hasChanged 对新旧值判断
- triggerRefValue(this)
convert(value):
- isObject(value) ? reactive(value) : value
createRef(value):
triggerRefValue(ref):
trackRefValue(ref):
对象:shallowUnwrapHandlers
- get(target, key, receiver):unRef(Reflect.get(target, key, receiver))
- set(target, key, value, receiver):Reflect.set(target, key, value, receiver)
proxyRefs(objectWithRefs):
- 作用:浅解包
- 主要逻辑:
- new Proxy(objectWithRefs, shallowUnwrapHandlers)
unRef(ref):
- isRef(ref) ? ref.value : ref
isRef(value):
computed
computed(getter):
- 作用:入口函数
- new ComputedRefImpl(getter)
class ComputedRefImpl:
constructor(getter):
- 主要逻辑:
- new ReactiveEffect(getter, scheduler)
- scheduler 中进行解锁,并执行 triggerRefValue 触发依赖
get value()
- 主要逻辑:
- 依赖收集
- 若上锁状态,则不执行 effect.run(),以此达到缓存的效果
- 即 computed 的依赖触发是在 get 而不是在 set
结尾
本文所提到的实现思路来自于大佬 cuixiaorui 的 mini-vue。 同时附上本人的 mini-vue 仓库。
|