]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
feat(reactivity): support onTrack/onTrigger debug options for computed
authorEvan You <yyx990803@gmail.com>
Thu, 8 Jul 2021 21:09:23 +0000 (17:09 -0400)
committerEvan You <yyx990803@gmail.com>
Fri, 16 Jul 2021 18:30:49 +0000 (14:30 -0400)
packages/reactivity/__tests__/computed.spec.ts
packages/reactivity/src/computed.ts
packages/reactivity/src/effect.ts
packages/reactivity/src/index.ts

index c4067722ed13112a2dfe824942cb46aae7d64b28..3f40b1c45ec634e2aeaa966793611990130a9138 100644 (file)
@@ -5,7 +5,12 @@ import {
   ref,
   WritableComputedRef,
   isReadonly,
-  setComputedScheduler
+  setComputedScheduler,
+  DebuggerEvent,
+  toRaw,
+  TrackOpTypes,
+  ITERATE_KEY,
+  TriggerOpTypes
 } from '../src'
 
 describe('reactivity/computed', () => {
@@ -200,6 +205,75 @@ describe('reactivity/computed', () => {
     expect(x.value).toBe(1)
   })
 
+  it('debug: onTrack', () => {
+    let events: DebuggerEvent[] = []
+    const onTrack = jest.fn((e: DebuggerEvent) => {
+      events.push(e)
+    })
+    const obj = reactive({ foo: 1, bar: 2 })
+    const c = computed(() => (obj.foo, 'bar' in obj, Object.keys(obj)), {
+      onTrack
+    })
+    expect(c.value).toEqual(['foo', 'bar'])
+    expect(onTrack).toHaveBeenCalledTimes(3)
+    expect(events).toEqual([
+      {
+        effect: c.effect,
+        target: toRaw(obj),
+        type: TrackOpTypes.GET,
+        key: 'foo'
+      },
+      {
+        effect: c.effect,
+        target: toRaw(obj),
+        type: TrackOpTypes.HAS,
+        key: 'bar'
+      },
+      {
+        effect: c.effect,
+        target: toRaw(obj),
+        type: TrackOpTypes.ITERATE,
+        key: ITERATE_KEY
+      }
+    ])
+  })
+
+  it('debug: onTrigger', () => {
+    let events: DebuggerEvent[] = []
+    const onTrigger = jest.fn((e: DebuggerEvent) => {
+      events.push(e)
+    })
+    const obj = reactive({ foo: 1 })
+    const c = computed(() => obj.foo, { onTrigger })
+
+    // computed won't trigger compute until accessed
+    c.value
+
+    obj.foo++
+    expect(c.value).toBe(2)
+    expect(onTrigger).toHaveBeenCalledTimes(1)
+    expect(events[0]).toEqual({
+      effect: c.effect,
+      target: toRaw(obj),
+      type: TriggerOpTypes.SET,
+      key: 'foo',
+      oldValue: 1,
+      newValue: 2
+    })
+
+    // @ts-ignore
+    delete obj.foo
+    expect(c.value).toBeUndefined()
+    expect(onTrigger).toHaveBeenCalledTimes(2)
+    expect(events[1]).toEqual({
+      effect: c.effect,
+      target: toRaw(obj),
+      type: TriggerOpTypes.DELETE,
+      key: 'foo',
+      oldValue: 2
+    })
+  })
+
   describe('with scheduler', () => {
     // a simple scheduler similar to the main Vue scheduler
     const tick = Promise.resolve()
index e1800f8c153c2084c5aaff46e1dfb3e3474fb344..c992031d5ffce77df93aa6d4d91856e33affd9ab 100644 (file)
@@ -1,4 +1,4 @@
-import { ReactiveEffect } from './effect'
+import { DebuggerOptions, ReactiveEffect } from './effect'
 import { Ref, trackRefValue, triggerRefValue } from './ref'
 import { isFunction, NOOP } from '@vue/shared'
 import { ReactiveFlags, toRaw } from './reactive'
@@ -101,12 +101,17 @@ class ComputedRefImpl<T> {
   }
 }
 
-export function computed<T>(getter: ComputedGetter<T>): ComputedRef<T>
 export function computed<T>(
-  options: WritableComputedOptions<T>
+  getter: ComputedGetter<T>,
+  debugOptions?: DebuggerOptions
+): ComputedRef<T>
+export function computed<T>(
+  options: WritableComputedOptions<T>,
+  debugOptions?: DebuggerOptions
 ): WritableComputedRef<T>
 export function computed<T>(
-  getterOrOptions: ComputedGetter<T> | WritableComputedOptions<T>
+  getterOrOptions: ComputedGetter<T> | WritableComputedOptions<T>,
+  debugOptions?: DebuggerOptions
 ) {
   let getter: ComputedGetter<T>
   let setter: ComputedSetter<T>
@@ -123,9 +128,16 @@ export function computed<T>(
     setter = getterOrOptions.set
   }
 
-  return new ComputedRefImpl(
+  const cRef = new ComputedRefImpl(
     getter,
     setter,
     isFunction(getterOrOptions) || !getterOrOptions.set
-  ) as any
+  )
+
+  if (__DEV__ && debugOptions) {
+    cRef.effect.onTrack = debugOptions.onTrack
+    cRef.effect.onTrigger = debugOptions.onTrigger
+  }
+
+  return cRef as any
 }
index 5c2ca83dadace79f520fc7fcfd6a618dd4cca4dd..231e15e60e17496b81e53120f5338fe93f2d13a4 100644 (file)
@@ -124,14 +124,17 @@ function cleanupEffect(effect: ReactiveEffect) {
   }
 }
 
-export interface ReactiveEffectOptions {
+export interface DebuggerOptions {
+  onTrack?: (event: DebuggerEvent) => void
+  onTrigger?: (event: DebuggerEvent) => void
+}
+
+export interface ReactiveEffectOptions extends DebuggerOptions {
   lazy?: boolean
   scheduler?: EffectScheduler
   scope?: EffectScope
   allowRecurse?: boolean
   onStop?: () => void
-  onTrack?: (event: DebuggerEvent) => void
-  onTrigger?: (event: DebuggerEvent) => void
 }
 
 export interface ReactiveEffectRunner<T = any> {
index 3d4b05730a5182984642ca815e7bbd239b72f826..776e4cedc533b4bc5e874e61f824e3b887ded5b5 100644 (file)
@@ -50,6 +50,7 @@ export {
   ReactiveEffectRunner,
   ReactiveEffectOptions,
   EffectScheduler,
+  DebuggerOptions,
   DebuggerEvent
 } from './effect'
 export {