ref,
WritableComputedRef,
isReadonly,
- setComputedScheduler
+ setComputedScheduler,
+ DebuggerEvent,
+ toRaw,
+ TrackOpTypes,
+ ITERATE_KEY,
+ TriggerOpTypes
} from '../src'
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()
-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'
}
}
-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>
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
}
}
}
-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> {
ReactiveEffectRunner,
ReactiveEffectOptions,
EffectScheduler,
+ DebuggerOptions,
DebuggerEvent
} from './effect'
export {