diff --git a/packages/reactivity/__tests__/effect.spec.ts b/packages/reactivity/__tests__/effect.spec.ts index 0e09e2e6d..a5a3a3728 100644 --- a/packages/reactivity/__tests__/effect.spec.ts +++ b/packages/reactivity/__tests__/effect.spec.ts @@ -3,7 +3,8 @@ import { effect, stop, toRaw, - OperationTypes, + TrackOpTypes, + TriggerOpTypes, DebuggerEvent, markNonReactive } from '../src/index' @@ -603,19 +604,19 @@ describe('reactivity/effect', () => { { effect: runner, target: toRaw(obj), - type: OperationTypes.GET, + type: TrackOpTypes.GET, key: 'foo' }, { effect: runner, target: toRaw(obj), - type: OperationTypes.HAS, + type: TrackOpTypes.HAS, key: 'bar' }, { effect: runner, target: toRaw(obj), - type: OperationTypes.ITERATE, + type: TrackOpTypes.ITERATE, key: ITERATE_KEY } ]) @@ -641,7 +642,7 @@ describe('reactivity/effect', () => { expect(events[0]).toEqual({ effect: runner, target: toRaw(obj), - type: OperationTypes.SET, + type: TriggerOpTypes.SET, key: 'foo', oldValue: 1, newValue: 2 @@ -653,7 +654,7 @@ describe('reactivity/effect', () => { expect(events[1]).toEqual({ effect: runner, target: toRaw(obj), - type: OperationTypes.DELETE, + type: TriggerOpTypes.DELETE, key: 'foo', oldValue: 2 }) diff --git a/packages/reactivity/src/baseHandlers.ts b/packages/reactivity/src/baseHandlers.ts index baff9231d..e37de1b65 100644 --- a/packages/reactivity/src/baseHandlers.ts +++ b/packages/reactivity/src/baseHandlers.ts @@ -1,6 +1,6 @@ import { reactive, readonly, toRaw } from './reactive' -import { OperationTypes } from './operations' -import { track, trigger } from './effect' +import { TrackOpTypes, TriggerOpTypes } from './operations' +import { track, trigger, ITERATE_KEY } from './effect' import { LOCKED } from './lock' import { isObject, hasOwn, isSymbol, hasChanged } from '@vue/shared' import { isRef } from './ref' @@ -18,14 +18,14 @@ function createGetter(isReadonly: boolean, shallow = false) { return res } if (shallow) { - track(target, OperationTypes.GET, key) + track(target, TrackOpTypes.GET, key) // TODO strict mode that returns a shallow-readonly version of the value return res } if (isRef(res)) { return res.value } - track(target, OperationTypes.GET, key) + track(target, TrackOpTypes.GET, key) return isObject(res) ? isReadonly ? // need to lazy access readonly and reactive here to avoid @@ -56,15 +56,15 @@ function set( if (__DEV__) { const extraInfo = { oldValue, newValue: value } if (!hadKey) { - trigger(target, OperationTypes.ADD, key, extraInfo) + trigger(target, TriggerOpTypes.ADD, key, extraInfo) } else if (hasChanged(value, oldValue)) { - trigger(target, OperationTypes.SET, key, extraInfo) + trigger(target, TriggerOpTypes.SET, key, extraInfo) } } else { if (!hadKey) { - trigger(target, OperationTypes.ADD, key) + trigger(target, TriggerOpTypes.ADD, key) } else if (hasChanged(value, oldValue)) { - trigger(target, OperationTypes.SET, key) + trigger(target, TriggerOpTypes.SET, key) } } } @@ -78,9 +78,9 @@ function deleteProperty(target: object, key: string | symbol): boolean { if (result && hadKey) { /* istanbul ignore else */ if (__DEV__) { - trigger(target, OperationTypes.DELETE, key, { oldValue }) + trigger(target, TriggerOpTypes.DELETE, key, { oldValue }) } else { - trigger(target, OperationTypes.DELETE, key) + trigger(target, TriggerOpTypes.DELETE, key) } } return result @@ -88,12 +88,12 @@ function deleteProperty(target: object, key: string | symbol): boolean { function has(target: object, key: string | symbol): boolean { const result = Reflect.has(target, key) - track(target, OperationTypes.HAS, key) + track(target, TrackOpTypes.HAS, key) return result } function ownKeys(target: object): (string | number | symbol)[] { - track(target, OperationTypes.ITERATE) + track(target, TrackOpTypes.ITERATE, ITERATE_KEY) return Reflect.ownKeys(target) } diff --git a/packages/reactivity/src/collectionHandlers.ts b/packages/reactivity/src/collectionHandlers.ts index 9e315261e..b52898440 100644 --- a/packages/reactivity/src/collectionHandlers.ts +++ b/packages/reactivity/src/collectionHandlers.ts @@ -1,6 +1,6 @@ import { toRaw, reactive, readonly } from './reactive' -import { track, trigger } from './effect' -import { OperationTypes } from './operations' +import { track, trigger, ITERATE_KEY } from './effect' +import { TrackOpTypes, TriggerOpTypes } from './operations' import { LOCKED } from './lock' import { isObject, capitalize, hasOwn, hasChanged } from '@vue/shared' @@ -27,20 +27,20 @@ function get( ) { target = toRaw(target) key = toRaw(key) - track(target, OperationTypes.GET, key) + track(target, TrackOpTypes.GET, key) return wrap(getProto(target).get.call(target, key)) } function has(this: CollectionTypes, key: unknown): boolean { const target = toRaw(this) key = toRaw(key) - track(target, OperationTypes.HAS, key) + track(target, TrackOpTypes.HAS, key) return getProto(target).has.call(target, key) } function size(target: IterableCollections) { target = toRaw(target) - track(target, OperationTypes.ITERATE) + track(target, TrackOpTypes.ITERATE, ITERATE_KEY) return Reflect.get(getProto(target), 'size', target) } @@ -53,9 +53,9 @@ function add(this: SetTypes, value: unknown) { if (!hadKey) { /* istanbul ignore else */ if (__DEV__) { - trigger(target, OperationTypes.ADD, value, { newValue: value }) + trigger(target, TriggerOpTypes.ADD, value, { newValue: value }) } else { - trigger(target, OperationTypes.ADD, value) + trigger(target, TriggerOpTypes.ADD, value) } } return result @@ -72,15 +72,15 @@ function set(this: MapTypes, key: unknown, value: unknown) { if (__DEV__) { const extraInfo = { oldValue, newValue: value } if (!hadKey) { - trigger(target, OperationTypes.ADD, key, extraInfo) + trigger(target, TriggerOpTypes.ADD, key, extraInfo) } else if (hasChanged(value, oldValue)) { - trigger(target, OperationTypes.SET, key, extraInfo) + trigger(target, TriggerOpTypes.SET, key, extraInfo) } } else { if (!hadKey) { - trigger(target, OperationTypes.ADD, key) + trigger(target, TriggerOpTypes.ADD, key) } else if (hasChanged(value, oldValue)) { - trigger(target, OperationTypes.SET, key) + trigger(target, TriggerOpTypes.SET, key) } } return result @@ -96,9 +96,9 @@ function deleteEntry(this: CollectionTypes, key: unknown) { if (hadKey) { /* istanbul ignore else */ if (__DEV__) { - trigger(target, OperationTypes.DELETE, key, { oldValue }) + trigger(target, TriggerOpTypes.DELETE, key, { oldValue }) } else { - trigger(target, OperationTypes.DELETE, key) + trigger(target, TriggerOpTypes.DELETE, key) } } return result @@ -117,9 +117,9 @@ function clear(this: IterableCollections) { if (hadItems) { /* istanbul ignore else */ if (__DEV__) { - trigger(target, OperationTypes.CLEAR, void 0, { oldTarget }) + trigger(target, TriggerOpTypes.CLEAR, void 0, { oldTarget }) } else { - trigger(target, OperationTypes.CLEAR) + trigger(target, TriggerOpTypes.CLEAR) } } return result @@ -134,7 +134,7 @@ function createForEach(isReadonly: boolean) { const observed = this const target = toRaw(observed) const wrap = isReadonly ? toReadonly : toReactive - track(target, OperationTypes.ITERATE) + track(target, TrackOpTypes.ITERATE, ITERATE_KEY) // important: create sure the callback is // 1. invoked with the reactive map as `this` and 3rd arg // 2. the value received should be a corresponding reactive/readonly. @@ -153,7 +153,7 @@ function createIterableMethod(method: string | symbol, isReadonly: boolean) { (method === Symbol.iterator && target instanceof Map) const innerIterator = getProto(target)[method].apply(target, args) const wrap = isReadonly ? toReadonly : toReactive - track(target, OperationTypes.ITERATE) + track(target, TrackOpTypes.ITERATE, ITERATE_KEY) // return a wrapped iterator which returns observed versions of the // values emitted from the real iterator return { @@ -177,7 +177,7 @@ function createIterableMethod(method: string | symbol, isReadonly: boolean) { function createReadonlyMethod( method: Function, - type: OperationTypes + type: TriggerOpTypes ): Function { return function(this: CollectionTypes, ...args: unknown[]) { if (LOCKED) { @@ -188,7 +188,7 @@ function createReadonlyMethod( toRaw(this) ) } - return type === OperationTypes.DELETE ? false : this + return type === TriggerOpTypes.DELETE ? false : this } else { return method.apply(this, args) } @@ -218,10 +218,10 @@ const readonlyInstrumentations: Record = { return size(this) }, has, - add: createReadonlyMethod(add, OperationTypes.ADD), - set: createReadonlyMethod(set, OperationTypes.SET), - delete: createReadonlyMethod(deleteEntry, OperationTypes.DELETE), - clear: createReadonlyMethod(clear, OperationTypes.CLEAR), + add: createReadonlyMethod(add, TriggerOpTypes.ADD), + set: createReadonlyMethod(set, TriggerOpTypes.SET), + delete: createReadonlyMethod(deleteEntry, TriggerOpTypes.DELETE), + clear: createReadonlyMethod(clear, TriggerOpTypes.CLEAR), forEach: createForEach(true) } diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index c71f65eaf..6dbf99cd0 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -1,7 +1,14 @@ -import { OperationTypes } from './operations' -import { Dep, targetMap } from './reactive' +import { TrackOpTypes, TriggerOpTypes } from './operations' import { EMPTY_OBJ, extend, isArray } from '@vue/shared' +// The main WeakMap that stores {target -> key -> dep} connections. +// Conceptually, it's easier to think of a dependency as a Dep class +// which maintains a Set of subscribers, but we simply store them as +// raw Sets to reduce memory overhead. +type Dep = Set +type KeyToDepMap = Map +const targetMap = new WeakMap() + export interface ReactiveEffect { (): T _isEffect: true @@ -23,7 +30,7 @@ export interface ReactiveEffectOptions { export type DebuggerEvent = { effect: ReactiveEffect target: object - type: OperationTypes + type: TrackOpTypes | TriggerOpTypes key: any } & DebuggerEventExtraInfo @@ -115,21 +122,18 @@ export function resumeTracking() { shouldTrack = true } -export function track(target: object, type: OperationTypes, key?: unknown) { +export function track(target: object, type: TrackOpTypes, key: unknown) { if (!shouldTrack || effectStack.length === 0) { return } const effect = effectStack[effectStack.length - 1] - if (type === OperationTypes.ITERATE) { - key = ITERATE_KEY - } let depsMap = targetMap.get(target) if (depsMap === void 0) { targetMap.set(target, (depsMap = new Map())) } - let dep = depsMap.get(key!) + let dep = depsMap.get(key) if (dep === void 0) { - depsMap.set(key!, (dep = new Set())) + depsMap.set(key, (dep = new Set())) } if (!dep.has(effect)) { dep.add(effect) @@ -147,7 +151,7 @@ export function track(target: object, type: OperationTypes, key?: unknown) { export function trigger( target: object, - type: OperationTypes, + type: TriggerOpTypes, key?: unknown, extraInfo?: DebuggerEventExtraInfo ) { @@ -158,7 +162,7 @@ export function trigger( } const effects = new Set() const computedRunners = new Set() - if (type === OperationTypes.CLEAR) { + if (type === TriggerOpTypes.CLEAR) { // collection being cleared, trigger all effects for target depsMap.forEach(dep => { addRunners(effects, computedRunners, dep) @@ -169,7 +173,7 @@ export function trigger( addRunners(effects, computedRunners, depsMap.get(key)) } // also run for iteration key on ADD | DELETE - if (type === OperationTypes.ADD || type === OperationTypes.DELETE) { + if (type === TriggerOpTypes.ADD || type === TriggerOpTypes.DELETE) { const iterationKey = isArray(target) ? 'length' : ITERATE_KEY addRunners(effects, computedRunners, depsMap.get(iterationKey)) } @@ -202,7 +206,7 @@ function addRunners( function scheduleRun( effect: ReactiveEffect, target: object, - type: OperationTypes, + type: TriggerOpTypes, key: unknown, extraInfo?: DebuggerEventExtraInfo ) { diff --git a/packages/reactivity/src/index.ts b/packages/reactivity/src/index.ts index e8a6e9bcf..05ad6594a 100644 --- a/packages/reactivity/src/index.ts +++ b/packages/reactivity/src/index.ts @@ -28,4 +28,4 @@ export { DebuggerEvent } from './effect' export { lock, unlock } from './lock' -export { OperationTypes } from './operations' +export { TrackOpTypes, TriggerOpTypes } from './operations' diff --git a/packages/reactivity/src/operations.ts b/packages/reactivity/src/operations.ts index 9f6ac5264..1b96e9825 100644 --- a/packages/reactivity/src/operations.ts +++ b/packages/reactivity/src/operations.ts @@ -1,11 +1,15 @@ -export const enum OperationTypes { - // using literal strings instead of numbers so that it's easier to inspect - // debugger events - SET = 'set', - ADD = 'add', - DELETE = 'delete', - CLEAR = 'clear', +// using literal strings instead of numbers so that it's easier to inspect +// debugger events + +export const enum TrackOpTypes { GET = 'get', HAS = 'has', ITERATE = 'iterate' } + +export const enum TriggerOpTypes { + SET = 'set', + ADD = 'add', + DELETE = 'delete', + CLEAR = 'clear' +} diff --git a/packages/reactivity/src/reactive.ts b/packages/reactivity/src/reactive.ts index f667c24db..091214481 100644 --- a/packages/reactivity/src/reactive.ts +++ b/packages/reactivity/src/reactive.ts @@ -8,18 +8,9 @@ import { mutableCollectionHandlers, readonlyCollectionHandlers } from './collectionHandlers' -import { ReactiveEffect } from './effect' import { UnwrapRef, Ref } from './ref' import { makeMap } from '@vue/shared' -// The main WeakMap that stores {target -> key -> dep} connections. -// Conceptually, it's easier to think of a dependency as a Dep class -// which maintains a Set of subscribers, but we simply store them as -// raw Sets to reduce memory overhead. -export type Dep = Set -export type KeyToDepMap = Map -export const targetMap = new WeakMap() - // WeakMaps that store {raw <-> observed} pairs. const rawToReactive = new WeakMap() const reactiveToRaw = new WeakMap() @@ -132,9 +123,6 @@ function createReactiveObject( observed = new Proxy(target, handlers) toProxy.set(target, observed) toRaw.set(observed, target) - if (!targetMap.has(target)) { - targetMap.set(target, new Map()) - } return observed } diff --git a/packages/reactivity/src/ref.ts b/packages/reactivity/src/ref.ts index b6c7835c4..e2ff79e18 100644 --- a/packages/reactivity/src/ref.ts +++ b/packages/reactivity/src/ref.ts @@ -1,5 +1,5 @@ import { track, trigger } from './effect' -import { OperationTypes } from './operations' +import { TrackOpTypes, TriggerOpTypes } from './operations' import { isObject } from '@vue/shared' import { reactive, isReactive } from './reactive' import { ComputedRef } from './computed' @@ -38,14 +38,14 @@ export function ref(raw?: unknown) { const r = { _isRef: true, get value() { - track(r, OperationTypes.GET, 'value') + track(r, TrackOpTypes.GET, 'value') return raw }, set value(newVal) { raw = convert(newVal) trigger( r, - OperationTypes.SET, + TriggerOpTypes.SET, 'value', __DEV__ ? { newValue: newVal } : void 0 ) diff --git a/packages/runtime-core/__tests__/apiLifecycle.spec.ts b/packages/runtime-core/__tests__/apiLifecycle.spec.ts index 91b7a1166..003a3d0a8 100644 --- a/packages/runtime-core/__tests__/apiLifecycle.spec.ts +++ b/packages/runtime-core/__tests__/apiLifecycle.spec.ts @@ -13,10 +13,10 @@ import { onUnmounted, onRenderTracked, reactive, - OperationTypes, + TrackOpTypes, onRenderTriggered } from '@vue/runtime-test' -import { ITERATE_KEY, DebuggerEvent } from '@vue/reactivity' +import { ITERATE_KEY, DebuggerEvent, TriggerOpTypes } from '@vue/reactivity' // reference: https://vue-composition-api-rfc.netlify.com/api.html#lifecycle-hooks @@ -283,17 +283,17 @@ describe('api: lifecycle hooks', () => { expect(events).toMatchObject([ { target: obj, - type: OperationTypes.GET, + type: TrackOpTypes.GET, key: 'foo' }, { target: obj, - type: OperationTypes.HAS, + type: TrackOpTypes.HAS, key: 'bar' }, { target: obj, - type: OperationTypes.ITERATE, + type: TrackOpTypes.ITERATE, key: ITERATE_KEY } ]) @@ -320,7 +320,7 @@ describe('api: lifecycle hooks', () => { await nextTick() expect(onTrigger).toHaveBeenCalledTimes(1) expect(events[0]).toMatchObject({ - type: OperationTypes.SET, + type: TriggerOpTypes.SET, key: 'foo', oldValue: 1, newValue: 2 @@ -330,7 +330,7 @@ describe('api: lifecycle hooks', () => { await nextTick() expect(onTrigger).toHaveBeenCalledTimes(2) expect(events[1]).toMatchObject({ - type: OperationTypes.DELETE, + type: TriggerOpTypes.DELETE, key: 'bar', oldValue: 2 }) @@ -338,7 +338,7 @@ describe('api: lifecycle hooks', () => { await nextTick() expect(onTrigger).toHaveBeenCalledTimes(3) expect(events[2]).toMatchObject({ - type: OperationTypes.ADD, + type: TriggerOpTypes.ADD, key: 'baz', newValue: 3 }) diff --git a/packages/runtime-core/__tests__/apiWatch.spec.ts b/packages/runtime-core/__tests__/apiWatch.spec.ts index 5f4dd323b..65710c65c 100644 --- a/packages/runtime-core/__tests__/apiWatch.spec.ts +++ b/packages/runtime-core/__tests__/apiWatch.spec.ts @@ -1,14 +1,11 @@ -import { - watch, - reactive, - computed, - nextTick, - ref, - h, - OperationTypes -} from '../src/index' +import { watch, reactive, computed, nextTick, ref, h } from '../src/index' import { render, nodeOps, serializeInner } from '@vue/runtime-test' -import { ITERATE_KEY, DebuggerEvent } from '@vue/reactivity' +import { + ITERATE_KEY, + DebuggerEvent, + TrackOpTypes, + TriggerOpTypes +} from '@vue/reactivity' // reference: https://vue-composition-api-rfc.netlify.com/api.html#watch @@ -366,17 +363,17 @@ describe('api: watch', () => { expect(events).toMatchObject([ { target: obj, - type: OperationTypes.GET, + type: TrackOpTypes.GET, key: 'foo' }, { target: obj, - type: OperationTypes.HAS, + type: TrackOpTypes.HAS, key: 'bar' }, { target: obj, - type: OperationTypes.ITERATE, + type: TrackOpTypes.ITERATE, key: ITERATE_KEY } ]) @@ -403,7 +400,7 @@ describe('api: watch', () => { expect(dummy).toBe(2) expect(onTrigger).toHaveBeenCalledTimes(1) expect(events[0]).toMatchObject({ - type: OperationTypes.SET, + type: TriggerOpTypes.SET, key: 'foo', oldValue: 1, newValue: 2 @@ -414,7 +411,7 @@ describe('api: watch', () => { expect(dummy).toBeUndefined() expect(onTrigger).toHaveBeenCalledTimes(2) expect(events[1]).toMatchObject({ - type: OperationTypes.DELETE, + type: TriggerOpTypes.DELETE, key: 'foo', oldValue: 2 }) diff --git a/packages/runtime-core/src/apiReactivity.ts b/packages/runtime-core/src/apiReactivity.ts index d60325724..026c3af67 100644 --- a/packages/runtime-core/src/apiReactivity.ts +++ b/packages/runtime-core/src/apiReactivity.ts @@ -14,7 +14,8 @@ export { ReactiveEffect, ReactiveEffectOptions, DebuggerEvent, - OperationTypes, + TrackOpTypes, + TriggerOpTypes, Ref, ComputedRef, UnwrapRef,