feat(reactivity): support onTrack/onTrigger debug options for computed
This commit is contained in:
		
							parent
							
								
									b7ea7c1485
								
							
						
					
					
						commit
						5cea9a1d4e
					
				@ -5,7 +5,12 @@ import {
 | 
				
			|||||||
  ref,
 | 
					  ref,
 | 
				
			||||||
  WritableComputedRef,
 | 
					  WritableComputedRef,
 | 
				
			||||||
  isReadonly,
 | 
					  isReadonly,
 | 
				
			||||||
  setComputedScheduler
 | 
					  setComputedScheduler,
 | 
				
			||||||
 | 
					  DebuggerEvent,
 | 
				
			||||||
 | 
					  toRaw,
 | 
				
			||||||
 | 
					  TrackOpTypes,
 | 
				
			||||||
 | 
					  ITERATE_KEY,
 | 
				
			||||||
 | 
					  TriggerOpTypes
 | 
				
			||||||
} from '../src'
 | 
					} from '../src'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('reactivity/computed', () => {
 | 
					describe('reactivity/computed', () => {
 | 
				
			||||||
@ -200,6 +205,75 @@ describe('reactivity/computed', () => {
 | 
				
			|||||||
    expect(x.value).toBe(1)
 | 
					    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', () => {
 | 
					  describe('with scheduler', () => {
 | 
				
			||||||
    // a simple scheduler similar to the main Vue scheduler
 | 
					    // a simple scheduler similar to the main Vue scheduler
 | 
				
			||||||
    const tick = Promise.resolve()
 | 
					    const tick = Promise.resolve()
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,4 @@
 | 
				
			|||||||
import { ReactiveEffect } from './effect'
 | 
					import { DebuggerOptions, ReactiveEffect } from './effect'
 | 
				
			||||||
import { Ref, trackRefValue, triggerRefValue } from './ref'
 | 
					import { Ref, trackRefValue, triggerRefValue } from './ref'
 | 
				
			||||||
import { isFunction, NOOP } from '@vue/shared'
 | 
					import { isFunction, NOOP } from '@vue/shared'
 | 
				
			||||||
import { ReactiveFlags, toRaw } from './reactive'
 | 
					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>(
 | 
					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>
 | 
					): WritableComputedRef<T>
 | 
				
			||||||
export function computed<T>(
 | 
					export function computed<T>(
 | 
				
			||||||
  getterOrOptions: ComputedGetter<T> | WritableComputedOptions<T>
 | 
					  getterOrOptions: ComputedGetter<T> | WritableComputedOptions<T>,
 | 
				
			||||||
 | 
					  debugOptions?: DebuggerOptions
 | 
				
			||||||
) {
 | 
					) {
 | 
				
			||||||
  let getter: ComputedGetter<T>
 | 
					  let getter: ComputedGetter<T>
 | 
				
			||||||
  let setter: ComputedSetter<T>
 | 
					  let setter: ComputedSetter<T>
 | 
				
			||||||
@ -123,9 +128,16 @@ export function computed<T>(
 | 
				
			|||||||
    setter = getterOrOptions.set
 | 
					    setter = getterOrOptions.set
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return new ComputedRefImpl(
 | 
					  const cRef = new ComputedRefImpl(
 | 
				
			||||||
    getter,
 | 
					    getter,
 | 
				
			||||||
    setter,
 | 
					    setter,
 | 
				
			||||||
    isFunction(getterOrOptions) || !getterOrOptions.set
 | 
					    isFunction(getterOrOptions) || !getterOrOptions.set
 | 
				
			||||||
  ) as any
 | 
					  )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (__DEV__ && debugOptions) {
 | 
				
			||||||
 | 
					    cRef.effect.onTrack = debugOptions.onTrack
 | 
				
			||||||
 | 
					    cRef.effect.onTrigger = debugOptions.onTrigger
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return cRef as any
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -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
 | 
					  lazy?: boolean
 | 
				
			||||||
  scheduler?: EffectScheduler
 | 
					  scheduler?: EffectScheduler
 | 
				
			||||||
  scope?: EffectScope
 | 
					  scope?: EffectScope
 | 
				
			||||||
  allowRecurse?: boolean
 | 
					  allowRecurse?: boolean
 | 
				
			||||||
  onStop?: () => void
 | 
					  onStop?: () => void
 | 
				
			||||||
  onTrack?: (event: DebuggerEvent) => void
 | 
					 | 
				
			||||||
  onTrigger?: (event: DebuggerEvent) => void
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface ReactiveEffectRunner<T = any> {
 | 
					export interface ReactiveEffectRunner<T = any> {
 | 
				
			||||||
 | 
				
			|||||||
@ -50,6 +50,7 @@ export {
 | 
				
			|||||||
  ReactiveEffectRunner,
 | 
					  ReactiveEffectRunner,
 | 
				
			||||||
  ReactiveEffectOptions,
 | 
					  ReactiveEffectOptions,
 | 
				
			||||||
  EffectScheduler,
 | 
					  EffectScheduler,
 | 
				
			||||||
 | 
					  DebuggerOptions,
 | 
				
			||||||
  DebuggerEvent
 | 
					  DebuggerEvent
 | 
				
			||||||
} from './effect'
 | 
					} from './effect'
 | 
				
			||||||
export {
 | 
					export {
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user