- 借助js单线程的特点,当调用effect的时候触发被收集过依赖的get,此时让属性记住依赖的effect,同理也让effect记住对应的属性
- 最外层使用到的数据结构 weakMap ,再到map,再到set存储当前的effect函数
- 稍后数据变化的时候 找到proxy的对象对应的map 通过key属性找到对应的set中的effect执行
观察者模式
vue3通过reactive或者ref去通过new Proxy的方式劫持属性的getter,跟vue2不一样,vue2是递归检测,vue3是懒代理的方式
发布订阅模式
当触发effect里面的回调方法时,vue会把这个activeEffect放置到公共空间,这时候会触发之前劫持过的属性的getter,这时候会把当前的activeEffect收集到对应属性的deps下,不过跟vue2不同的是,vue3是通过一个公共对象targetMap去实现的,他实际上是一个weakMap
export function track(target: object, type: TrackOpTypes, key: unknown) { if (shouldTrack && activeEffect) { let depsMap = targetMap.get(target) if (!depsMap) { targetMap.set(target, (depsMap = new Map())) } let dep = depsMap.get(key) if (!dep) { depsMap.set(key, (dep = createDep())) } const eventInfo = __DEV__ ? { effect: activeEffect, target, type, key } : undefined trackEffects(dep, eventInfo) } }
- 按照以上的方法去将当前的effect收集到公共对象targetMap里面
- targetMap的key是每个传入到reactive方法里面的对象
- targetMap的value(depMap)是对应的这一个reactived对象的里面每一个key的effect集合(是一个map)
- effect集合里面是一堆Set,使用set刚好可以过滤掉一些相同的effect实例
例如:const a = reactive({a:1})
这样当修改了reactived的对象,就会触发trigger方法,然后依次将收集到的effect去依次执行,触发发布
export function trigger( target: object, type: TriggerOpTypes, key?: unknown, newValue?: unknown, oldValue?: unknown, oldTarget?: Map<unknown, unknown> | Set<unknown> ) { const depsMap = targetMap.get(target) if (!depsMap) { // never been tracked return } let deps: (Dep | undefined)[] = [] if (type === TriggerOpTypes.CLEAR) { // collection being cleared // trigger all effects for target deps = [...depsMap.values()] } else if (key === 'length' && isArray(target)) { depsMap.forEach((dep, key) => { if (key === 'length' || key >= (newValue as number)) { deps.push(dep) } }) } else { // schedule runs for SET | ADD | DELETE if (key !== void 0) { deps.push(depsMap.get(key)) } // also run for iteration key on ADD | DELETE | Map.SET switch (type) { case TriggerOpTypes.ADD: if (!isArray(target)) { deps.push(depsMap.get(ITERATE_KEY)) if (isMap(target)) { deps.push(depsMap.get(MAP_KEY_ITERATE_KEY)) } } else if (isIntegerKey(key)) { // new index added to array -> length changes deps.push(depsMap.get('length')) } break case TriggerOpTypes.DELETE: if (!isArray(target)) { deps.push(depsMap.get(ITERATE_KEY)) if (isMap(target)) { deps.push(depsMap.get(MAP_KEY_ITERATE_KEY)) } } break case TriggerOpTypes.SET: if (isMap(target)) { deps.push(depsMap.get(ITERATE_KEY)) } break } } const eventInfo = __DEV__ ? { target, type, key, newValue, oldValue, oldTarget } : undefined if (deps.length === 1) { if (deps[0]) { if (__DEV__) { triggerEffects(deps[0], eventInfo) } else { triggerEffects(deps[0]) } } } else { const effects: ReactiveEffect[] = [] for (const dep of deps) { if (dep) { effects.push(...dep) } } if (__DEV__) { triggerEffects(createDep(effects), eventInfo) } else { triggerEffects(createDep(effects)) } } }