Skip to content

Commit 5cea9a1

Browse files
committed
feat(reactivity): support onTrack/onTrigger debug options for computed
1 parent b7ea7c1 commit 5cea9a1

File tree

4 files changed

+100
-10
lines changed

4 files changed

+100
-10
lines changed

packages/reactivity/__tests__/computed.spec.ts

+75-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,12 @@ import {
55
ref,
66
WritableComputedRef,
77
isReadonly,
8-
setComputedScheduler
8+
setComputedScheduler,
9+
DebuggerEvent,
10+
toRaw,
11+
TrackOpTypes,
12+
ITERATE_KEY,
13+
TriggerOpTypes
914
} from '../src'
1015

1116
describe('reactivity/computed', () => {
@@ -200,6 +205,75 @@ describe('reactivity/computed', () => {
200205
expect(x.value).toBe(1)
201206
})
202207

208+
it('debug: onTrack', () => {
209+
let events: DebuggerEvent[] = []
210+
const onTrack = jest.fn((e: DebuggerEvent) => {
211+
events.push(e)
212+
})
213+
const obj = reactive({ foo: 1, bar: 2 })
214+
const c = computed(() => (obj.foo, 'bar' in obj, Object.keys(obj)), {
215+
onTrack
216+
})
217+
expect(c.value).toEqual(['foo', 'bar'])
218+
expect(onTrack).toHaveBeenCalledTimes(3)
219+
expect(events).toEqual([
220+
{
221+
effect: c.effect,
222+
target: toRaw(obj),
223+
type: TrackOpTypes.GET,
224+
key: 'foo'
225+
},
226+
{
227+
effect: c.effect,
228+
target: toRaw(obj),
229+
type: TrackOpTypes.HAS,
230+
key: 'bar'
231+
},
232+
{
233+
effect: c.effect,
234+
target: toRaw(obj),
235+
type: TrackOpTypes.ITERATE,
236+
key: ITERATE_KEY
237+
}
238+
])
239+
})
240+
241+
it('debug: onTrigger', () => {
242+
let events: DebuggerEvent[] = []
243+
const onTrigger = jest.fn((e: DebuggerEvent) => {
244+
events.push(e)
245+
})
246+
const obj = reactive({ foo: 1 })
247+
const c = computed(() => obj.foo, { onTrigger })
248+
249+
// computed won't trigger compute until accessed
250+
c.value
251+
252+
obj.foo++
253+
expect(c.value).toBe(2)
254+
expect(onTrigger).toHaveBeenCalledTimes(1)
255+
expect(events[0]).toEqual({
256+
effect: c.effect,
257+
target: toRaw(obj),
258+
type: TriggerOpTypes.SET,
259+
key: 'foo',
260+
oldValue: 1,
261+
newValue: 2
262+
})
263+
264+
// @ts-ignore
265+
delete obj.foo
266+
expect(c.value).toBeUndefined()
267+
expect(onTrigger).toHaveBeenCalledTimes(2)
268+
expect(events[1]).toEqual({
269+
effect: c.effect,
270+
target: toRaw(obj),
271+
type: TriggerOpTypes.DELETE,
272+
key: 'foo',
273+
oldValue: 2
274+
})
275+
})
276+
203277
describe('with scheduler', () => {
204278
// a simple scheduler similar to the main Vue scheduler
205279
const tick = Promise.resolve()

packages/reactivity/src/computed.ts

+18-6
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ReactiveEffect } from './effect'
1+
import { DebuggerOptions, ReactiveEffect } from './effect'
22
import { Ref, trackRefValue, triggerRefValue } from './ref'
33
import { isFunction, NOOP } from '@vue/shared'
44
import { ReactiveFlags, toRaw } from './reactive'
@@ -101,12 +101,17 @@ class ComputedRefImpl<T> {
101101
}
102102
}
103103

104-
export function computed<T>(getter: ComputedGetter<T>): ComputedRef<T>
105104
export function computed<T>(
106-
options: WritableComputedOptions<T>
105+
getter: ComputedGetter<T>,
106+
debugOptions?: DebuggerOptions
107+
): ComputedRef<T>
108+
export function computed<T>(
109+
options: WritableComputedOptions<T>,
110+
debugOptions?: DebuggerOptions
107111
): WritableComputedRef<T>
108112
export function computed<T>(
109-
getterOrOptions: ComputedGetter<T> | WritableComputedOptions<T>
113+
getterOrOptions: ComputedGetter<T> | WritableComputedOptions<T>,
114+
debugOptions?: DebuggerOptions
110115
) {
111116
let getter: ComputedGetter<T>
112117
let setter: ComputedSetter<T>
@@ -123,9 +128,16 @@ export function computed<T>(
123128
setter = getterOrOptions.set
124129
}
125130

126-
return new ComputedRefImpl(
131+
const cRef = new ComputedRefImpl(
127132
getter,
128133
setter,
129134
isFunction(getterOrOptions) || !getterOrOptions.set
130-
) as any
135+
)
136+
137+
if (__DEV__ && debugOptions) {
138+
cRef.effect.onTrack = debugOptions.onTrack
139+
cRef.effect.onTrigger = debugOptions.onTrigger
140+
}
141+
142+
return cRef as any
131143
}

packages/reactivity/src/effect.ts

+6-3
Original file line numberDiff line numberDiff line change
@@ -124,14 +124,17 @@ function cleanupEffect(effect: ReactiveEffect) {
124124
}
125125
}
126126

127-
export interface ReactiveEffectOptions {
127+
export interface DebuggerOptions {
128+
onTrack?: (event: DebuggerEvent) => void
129+
onTrigger?: (event: DebuggerEvent) => void
130+
}
131+
132+
export interface ReactiveEffectOptions extends DebuggerOptions {
128133
lazy?: boolean
129134
scheduler?: EffectScheduler
130135
scope?: EffectScope
131136
allowRecurse?: boolean
132137
onStop?: () => void
133-
onTrack?: (event: DebuggerEvent) => void
134-
onTrigger?: (event: DebuggerEvent) => void
135138
}
136139

137140
export interface ReactiveEffectRunner<T = any> {

packages/reactivity/src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ export {
5050
ReactiveEffectRunner,
5151
ReactiveEffectOptions,
5252
EffectScheduler,
53+
DebuggerOptions,
5354
DebuggerEvent
5455
} from './effect'
5556
export {

0 commit comments

Comments
 (0)