Open zhangzheng-zz opened 3 years ago
参考:https://www.gitsu.cn/detail?id=82
Vue3响应式系统依旧通过数据劫持的方式,改用Proxy做代理。取值的时候track,赋值的时候trigger,effect将正在调用这个对象的函数activeEffect和响应式对象key联系起来实现响应式系统。
Vue3
Proxy
track
trigger
effect
activeEffect
key
function createReactiveObject( target: Target, isReadonly: boolean, baseHandlers: ProxyHandler<any>, collectionHandlers: ProxyHandler<any> ) { // ... const proxy = new Proxy( target, targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers ) return proxy }
get && set
get
set
function createGetter(isReadonly = false, shallow = false) { return function get(target: Target, key: string | symbol, receiver: object) { const res = Reflect.get(target, key, receiver) if (!isReadonly) { track(target, TrackOpTypes.GET, key) } return res } } function createSetter(shallow = false) { return function set( target: object, key: string | symbol, value: unknown, receiver: object ): boolean { const result = Reflect.set(target, key, value, receiver) // don't trigger if target is something up in the prototype chain of original if (target === toRaw(receiver)) { if (!hadKey) { trigger(target, TriggerOpTypes.ADD, key, value) } else if (hasChanged(value, oldValue)) { trigger(target, TriggerOpTypes.SET, key, value, oldValue) } } return result } }
track&trigger
export function track(target: object, type: TrackOpTypes, key: unknown) { let depsMap = targetMap.get(target) if (!depsMap) { targetMap.set(target, (depsMap = new Map())) } let dep = depsMap.get(key) if (!dep) { depsMap.set(key, (dep = new Set())) } //activeEffect 正在调用这个对象的函数 if (!dep.has(activeEffect)) { dep.add(activeEffect) activeEffect.deps.push(dep) } } export function trigger( target: object, type: TriggerOpTypes, key?: unknown, newValue?: unknown, oldValue?: unknown, oldTarget?: Map<unknown, unknown> | Set<unknown> ) { const depsMap = targetMap.get(target) const effects = new Set<ReactiveEffect>() // 有 scheduler 就先入队 queue if (effect.options.scheduler) { effect.options.scheduler(effect) } else { effect() } } effects.forEach(run) }
三个存储变量:targetMap (WeakMap) 对象 -> depsMap (Map) 依赖 -> dep(Set) 副作用
targetMap (WeakMap) 对象 -> depsMap (Map) 依赖 -> dep(Set) 副作用
api
我们使用 get、set、has等这些 api 的时候会触发 get,拿到的是这些操作函数,我们使用函数劫持,只要劫持一个 get方法(即对对象的key的访问)并替换成自己封装的方法。
export const mutableCollectionHandlers: ProxyHandler<CollectionTypes> = { get: createInstrumentationGetter(false, false) } const mutableInstrumentations: Record<string, Function> = { get(this: MapTypes, key: unknown) { return get(this, key) }, get size() { return size((this as unknown) as IterableCollections) }, has, add, set, delete: deleteEntry, clear, forEach: createForEach(false, false) }
Vue3 使用 Proxy:
index
length
// new index added to array -> length changes add(depsMap.get('length'))
参考:https://www.gitsu.cn/detail?id=82
响应式系统:
Vue3
响应式系统依旧通过数据劫持的方式,改用Proxy
做代理。取值的时候track
,赋值的时候trigger
,effect
将正在调用这个对象的函数activeEffect
和响应式对象key
联系起来实现响应式系统。reactive
get
&&set
track
&trigger
三个存储变量:
targetMap (WeakMap) 对象 -> depsMap (Map) 依赖 -> dep(Set) 副作用
对于 Map WeakMap Set WeakSet 的各种
api
操作的劫持怎么实现?我们使用 get、set、has等这些
api
的时候会触发get
,拿到的是这些操作函数,我们使用函数劫持,只要劫持一个get
方法(即对对象的key
的访问)并替换成自己封装的方法。Vue2 vs Vue3
Vue3 使用 Proxy:
key
是index
,此时index
还未被track
,怎么监听? 将effect
绑定到length
这个key
// new index added to array -> length changes add(depsMap.get('length'))