]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
refactor(reactivity): separate track and trigger operation types
authorEvan You <yyx990803@gmail.com>
Tue, 3 Dec 2019 16:30:24 +0000 (11:30 -0500)
committerEvan You <yyx990803@gmail.com>
Tue, 3 Dec 2019 16:30:24 +0000 (11:30 -0500)
packages/reactivity/__tests__/effect.spec.ts
packages/reactivity/src/baseHandlers.ts
packages/reactivity/src/collectionHandlers.ts
packages/reactivity/src/effect.ts
packages/reactivity/src/index.ts
packages/reactivity/src/operations.ts
packages/reactivity/src/reactive.ts
packages/reactivity/src/ref.ts
packages/runtime-core/__tests__/apiLifecycle.spec.ts
packages/runtime-core/__tests__/apiWatch.spec.ts
packages/runtime-core/src/apiReactivity.ts

index 0e09e2e6d4248706861a5a62063705b3d3697ca5..a5a3a372838ea01052ce830a3f47251d4d0de57f 100644 (file)
@@ -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
     })
index baff9231dd0ff64bf6cff0be4f84f5b2cf1423f9..e37de1b65ad384c8343c98429853921ecaba3e60 100644 (file)
@@ -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)
 }
 
index 9e315261e64851df6750afa49bcf73a0d802092e..b5289844089a409ab8e738ef542d469c09b6e90f 100644 (file)
@@ -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<string, Function> = {
     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)
 }
 
index c71f65eaf3f79c42ee54a5bce2d986089eacadcb..6dbf99cd01a7cba37fd0c8de5243485d784bc870 100644 (file)
@@ -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<ReactiveEffect>
+type KeyToDepMap = Map<any, Dep>
+const targetMap = new WeakMap<any, KeyToDepMap>()
+
 export interface ReactiveEffect<T = any> {
   (): 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<ReactiveEffect>()
   const computedRunners = new Set<ReactiveEffect>()
-  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
 ) {
index e8a6e9bcf0c77a7ff52510fe0367eeba97544db7..05ad6594a90de10bd53317b4277973c58cb293c1 100644 (file)
@@ -28,4 +28,4 @@ export {
   DebuggerEvent
 } from './effect'
 export { lock, unlock } from './lock'
-export { OperationTypes } from './operations'
+export { TrackOpTypes, TriggerOpTypes } from './operations'
index 9f6ac5264c633d270446de23198bc13825ff3f63..1b96e982571b31ad53200b647b276c42f968d052 100644 (file)
@@ -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'
+}
index f667c24db772c75895499ce58ec3ac3db110a7de..091214481dd93d13a2c86b67ca28507f42b89e0a 100644 (file)
@@ -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<ReactiveEffect>
-export type KeyToDepMap = Map<any, Dep>
-export const targetMap = new WeakMap<any, KeyToDepMap>()
-
 // WeakMaps that store {raw <-> observed} pairs.
 const rawToReactive = new WeakMap<any, any>()
 const reactiveToRaw = new WeakMap<any, any>()
@@ -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
 }
 
index b6c7835c4b4d1faadc6e95d47311b6be727addc9..e2ff79e185961fc828834364a0da9814f18af8d7 100644 (file)
@@ -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
       )
index 91b7a116698e6d717c0213174a5ce89601a96fc3..003a3d0a8dde7ea9f1c7cdecc7f6f74ba8f9f80c 100644 (file)
@@ -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
     })
index 5f4dd323b163fb85d8f47853066b76c4d6acabc0..65710c65c55cd9b73af5bff25cf18ea56f19b740 100644 (file)
@@ -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
     })
index d603257249bd8db88800df1eab71b2d1f877d9f3..026c3af677a4733d592b17f4e36755ef69554067 100644 (file)
@@ -14,7 +14,8 @@ export {
   ReactiveEffect,
   ReactiveEffectOptions,
   DebuggerEvent,
-  OperationTypes,
+  TrackOpTypes,
+  TriggerOpTypes,
   Ref,
   ComputedRef,
   UnwrapRef,