mirror of
https://gitee.com/vuejs/vue.git
synced 2024-11-30 02:57:43 +08:00
wip: onTrack debugger option
This commit is contained in:
parent
9fb4f7d070
commit
15c5c43ca6
@ -30,6 +30,7 @@ import {
|
||||
isFunction
|
||||
} from '../util/index'
|
||||
import type { Component } from 'typescript/component'
|
||||
import { TrackOpTypes } from '../../v3'
|
||||
|
||||
const sharedPropertyDefinition = {
|
||||
enumerable: true,
|
||||
@ -254,6 +255,14 @@ function createComputedGetter(key) {
|
||||
watcher.evaluate()
|
||||
}
|
||||
if (Dep.target) {
|
||||
if (__DEV__ && Dep.target.onTrack) {
|
||||
Dep.target.onTrack({
|
||||
effect: Dep.target,
|
||||
target: this,
|
||||
type: TrackOpTypes.GET,
|
||||
key
|
||||
})
|
||||
}
|
||||
watcher.depend()
|
||||
}
|
||||
return watcher.value
|
||||
|
@ -1,14 +1,33 @@
|
||||
import { remove } from '../util/index'
|
||||
import config from '../config'
|
||||
import { TrackOpTypes, TriggerOpTypes } from 'v3'
|
||||
|
||||
let uid = 0
|
||||
|
||||
export interface DepTarget {
|
||||
export interface DepTarget extends DebuggerOptions {
|
||||
id: number
|
||||
addDep(dep: Dep): void
|
||||
update(): void
|
||||
}
|
||||
|
||||
export interface DebuggerOptions {
|
||||
onTrack?: (event: DebuggerEvent) => void
|
||||
onTrigger?: (event: DebuggerEvent) => void
|
||||
}
|
||||
|
||||
export type DebuggerEvent = {
|
||||
effect: DepTarget
|
||||
} & DebuggerEventExtraInfo
|
||||
|
||||
export type DebuggerEventExtraInfo = {
|
||||
target: object
|
||||
type: TrackOpTypes | TriggerOpTypes
|
||||
key?: any
|
||||
newValue?: any
|
||||
oldValue?: any
|
||||
oldTarget?: Map<any, any> | Set<any>
|
||||
}
|
||||
|
||||
/**
|
||||
* A dep is an observable that can have multiple
|
||||
* directives subscribing to it.
|
||||
@ -31,13 +50,19 @@ export default class Dep {
|
||||
remove(this.subs, sub)
|
||||
}
|
||||
|
||||
depend() {
|
||||
depend(info?: DebuggerEventExtraInfo) {
|
||||
if (Dep.target) {
|
||||
Dep.target.addDep(this)
|
||||
if (__DEV__ && info && Dep.target.onTrack) {
|
||||
Dep.target.onTrack({
|
||||
effect: Dep.target,
|
||||
...info
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
notify() {
|
||||
notify(info?: DebuggerEventExtraInfo) {
|
||||
// stabilize the subscriber list first
|
||||
const subs = this.subs.slice()
|
||||
if (__DEV__ && !config.async) {
|
||||
@ -47,6 +72,14 @@ export default class Dep {
|
||||
subs.sort((a, b) => a.id - b.id)
|
||||
}
|
||||
for (let i = 0, l = subs.length; i < l; i++) {
|
||||
if (__DEV__ && info) {
|
||||
const sub = subs[i]
|
||||
sub.onTrigger &&
|
||||
sub.onTrigger({
|
||||
effect: subs[i],
|
||||
...info
|
||||
})
|
||||
}
|
||||
subs[i].update()
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ import {
|
||||
isServerRendering,
|
||||
hasChanged
|
||||
} from '../util/index'
|
||||
import { isReadonly, isRef } from '../../v3'
|
||||
import { isReadonly, isRef, TrackOpTypes } from '../../v3'
|
||||
|
||||
const arrayKeys = Object.getOwnPropertyNames(arrayMethods)
|
||||
|
||||
@ -165,7 +165,15 @@ export function defineReactive(
|
||||
get: function reactiveGetter() {
|
||||
const value = getter ? getter.call(obj) : val
|
||||
if (Dep.target) {
|
||||
dep.depend()
|
||||
if (__DEV__) {
|
||||
dep.depend({
|
||||
target: obj,
|
||||
type: TrackOpTypes.GET,
|
||||
key
|
||||
})
|
||||
} else {
|
||||
dep.depend()
|
||||
}
|
||||
if (childOb) {
|
||||
childOb.dep.depend()
|
||||
if (isArray(value)) {
|
||||
@ -291,7 +299,9 @@ export function del(target: Array<any> | Object, key: any) {
|
||||
function dependArray(value: Array<any>) {
|
||||
for (let e, i = 0, l = value.length; i < l; i++) {
|
||||
e = value[i]
|
||||
e && e.__ob__ && e.__ob__.dep.depend()
|
||||
if (e && e.__ob__) {
|
||||
e.__ob__.dep.depend()
|
||||
}
|
||||
if (isArray(e)) {
|
||||
dependArray(e)
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ import {
|
||||
|
||||
import { traverse } from './traverse'
|
||||
import { queueWatcher } from './scheduler'
|
||||
import Dep, { pushTarget, popTarget, DepTarget } from './dep'
|
||||
import Dep, { pushTarget, popTarget, DepTarget, DebuggerEvent } from './dep'
|
||||
|
||||
import type { SimpleSet } from '../util/index'
|
||||
import type { Component } from 'typescript/component'
|
||||
@ -49,6 +49,10 @@ export default class Watcher implements DepTarget {
|
||||
getter: Function
|
||||
value: any
|
||||
|
||||
// dev only
|
||||
onTrack?: ((event: DebuggerEvent) => void) | undefined
|
||||
onTrigger?: ((event: DebuggerEvent) => void) | undefined
|
||||
|
||||
constructor(
|
||||
vm: Component | null,
|
||||
expOrFn: string | (() => any),
|
||||
|
@ -15,7 +15,6 @@ if (__DEV__) {
|
||||
str.replace(classifyRE, c => c.toUpperCase()).replace(/[-_]/g, '')
|
||||
|
||||
warn = (msg, vm = currentInstance) => {
|
||||
// TODO get current instance
|
||||
const trace = vm ? generateComponentTrace(vm) : ''
|
||||
|
||||
if (config.warnHandler) {
|
||||
|
@ -15,7 +15,7 @@ import { currentInstance } from './currentInstance'
|
||||
import { traverse } from 'core/observer/traverse'
|
||||
import Watcher from '../core/observer/watcher'
|
||||
import { queueWatcher } from '../core/observer/scheduler'
|
||||
import { TrackOpTypes, TriggerOpTypes } from './reactivity/operations'
|
||||
import { DebuggerOptions } from '../core/observer/dep'
|
||||
|
||||
const WATCHER = `watcher`
|
||||
const WATCHER_CB = `${WATCHER} callback`
|
||||
@ -50,24 +50,6 @@ export interface WatchOptionsBase extends DebuggerOptions {
|
||||
flush?: 'pre' | 'post' | 'sync'
|
||||
}
|
||||
|
||||
export interface DebuggerOptions {
|
||||
onTrack?: (event: DebuggerEvent) => void
|
||||
onTrigger?: (event: DebuggerEvent) => void
|
||||
}
|
||||
|
||||
export type DebuggerEvent = {
|
||||
watcher: Watcher
|
||||
} & DebuggerEventExtraInfo
|
||||
|
||||
export type DebuggerEventExtraInfo = {
|
||||
target: object
|
||||
type: TrackOpTypes | TriggerOpTypes
|
||||
key: any
|
||||
newValue?: any
|
||||
oldValue?: any
|
||||
oldTarget?: Map<any, any> | Set<any>
|
||||
}
|
||||
|
||||
export interface WatchOptions<Immediate = boolean> extends WatchOptionsBase {
|
||||
immediate?: Immediate
|
||||
deep?: boolean
|
||||
@ -349,11 +331,10 @@ function doWatch(
|
||||
}
|
||||
}
|
||||
|
||||
// TODO
|
||||
// if (__DEV__) {
|
||||
// effect.onTrack = onTrack
|
||||
// effect.onTrigger = onTrigger
|
||||
// }
|
||||
if (__DEV__) {
|
||||
watcher.onTrack = onTrack
|
||||
watcher.onTrigger = onTrigger
|
||||
}
|
||||
|
||||
// initial run
|
||||
if (cb) {
|
||||
@ -370,9 +351,5 @@ function doWatch(
|
||||
|
||||
return () => {
|
||||
watcher.teardown()
|
||||
// TODO
|
||||
// if (instance && instance.scope) {
|
||||
// remove(instance.scope.effects!, effect)
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
@ -52,9 +52,7 @@ export {
|
||||
WatchOptionsBase,
|
||||
WatchCallback,
|
||||
WatchSource,
|
||||
WatchStopHandle,
|
||||
DebuggerOptions,
|
||||
DebuggerEvent
|
||||
WatchStopHandle
|
||||
} from './apiWatch'
|
||||
|
||||
export {
|
||||
@ -64,6 +62,11 @@ export {
|
||||
getCurrentScope
|
||||
} from './reactivity/effectScope'
|
||||
|
||||
export {
|
||||
DebuggerOptions,
|
||||
DebuggerEvent,
|
||||
DebuggerEventExtraInfo
|
||||
} from 'core/observer/dep'
|
||||
export { TrackOpTypes, TriggerOpTypes } from './reactivity/operations'
|
||||
|
||||
export { h } from './h'
|
||||
|
@ -1,10 +1,10 @@
|
||||
import { isServerRendering, noop, warn, def, isFunction } from 'core/util'
|
||||
import { Ref, RefFlag } from './ref'
|
||||
import Watcher from 'core/observer/watcher'
|
||||
import Dep from 'core/observer/dep'
|
||||
import Dep, { DebuggerOptions } from 'core/observer/dep'
|
||||
import { currentInstance } from '../currentInstance'
|
||||
import { DebuggerOptions } from '../apiWatch'
|
||||
import { ReactiveFlags } from './reactive'
|
||||
import { TrackOpTypes } from './operations'
|
||||
|
||||
declare const ComputedRefSymbol: unique symbol
|
||||
|
||||
@ -35,7 +35,6 @@ export function computed<T>(
|
||||
): WritableComputedRef<T>
|
||||
export function computed<T>(
|
||||
getterOrOptions: ComputedGetter<T> | WritableComputedOptions<T>,
|
||||
// TODO debug options
|
||||
debugOptions?: DebuggerOptions
|
||||
) {
|
||||
let getter: ComputedGetter<T>
|
||||
@ -58,6 +57,11 @@ export function computed<T>(
|
||||
? null
|
||||
: new Watcher(currentInstance, getter, noop, { lazy: true })
|
||||
|
||||
if (__DEV__ && watcher && debugOptions) {
|
||||
watcher.onTrack = debugOptions.onTrack
|
||||
watcher.onTrigger = debugOptions.onTrigger
|
||||
}
|
||||
|
||||
const ref = {
|
||||
// some libs rely on the presence effect for checking computed refs
|
||||
// from normal refs, but the implementation doesn't matter
|
||||
@ -68,6 +72,14 @@ export function computed<T>(
|
||||
watcher.evaluate()
|
||||
}
|
||||
if (Dep.target) {
|
||||
if (__DEV__ && Dep.target.onTrack) {
|
||||
Dep.target.onTrack({
|
||||
effect: Dep.target,
|
||||
target: ref,
|
||||
type: TrackOpTypes.GET,
|
||||
key: 'value'
|
||||
})
|
||||
}
|
||||
watcher.depend()
|
||||
}
|
||||
return watcher.value
|
||||
|
@ -3,13 +3,11 @@
|
||||
|
||||
export const enum TrackOpTypes {
|
||||
GET = 'get',
|
||||
HAS = 'has',
|
||||
ITERATE = 'iterate'
|
||||
TOUCH = 'touch'
|
||||
}
|
||||
|
||||
export const enum TriggerOpTypes {
|
||||
SET = 'set',
|
||||
ADD = 'add',
|
||||
DELETE = 'delete',
|
||||
CLEAR = 'clear'
|
||||
DELETE = 'delete'
|
||||
}
|
||||
|
@ -93,7 +93,6 @@ export function isShallow(value: unknown): boolean {
|
||||
}
|
||||
|
||||
export function isReadonly(value: unknown): boolean {
|
||||
// TODO
|
||||
return !!(value && (value as Target).__v_isReadonly)
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,7 @@ import {
|
||||
import type { IfAny } from 'typescript/utils'
|
||||
import Dep from 'core/observer/dep'
|
||||
import { warn, isArray, def } from 'core/util'
|
||||
import { TrackOpTypes } from './operations'
|
||||
|
||||
declare const RefSymbol: unique symbol
|
||||
export declare const RawSymbol: unique symbol
|
||||
@ -91,8 +92,20 @@ export type CustomRefFactory<T> = (
|
||||
export function customRef<T>(factory: CustomRefFactory<T>): Ref<T> {
|
||||
const dep = new Dep()
|
||||
const { get, set } = factory(
|
||||
() => dep.depend(),
|
||||
() => dep.notify()
|
||||
() => {
|
||||
if (__DEV__) {
|
||||
dep.depend({
|
||||
target: ref,
|
||||
type: TrackOpTypes.GET,
|
||||
key: 'value'
|
||||
})
|
||||
} else {
|
||||
dep.depend()
|
||||
}
|
||||
},
|
||||
() => {
|
||||
dep.notify()
|
||||
}
|
||||
)
|
||||
const ref = {
|
||||
get value() {
|
||||
|
@ -12,7 +12,10 @@ import {
|
||||
h,
|
||||
onMounted,
|
||||
getCurrentInstance,
|
||||
effectScope
|
||||
effectScope,
|
||||
TrackOpTypes,
|
||||
TriggerOpTypes,
|
||||
DebuggerEvent
|
||||
} from 'v3'
|
||||
import { nextTick } from 'core/util'
|
||||
import { set } from 'core/observer'
|
||||
@ -777,41 +780,43 @@ describe('api: watch', () => {
|
||||
expect(`"deep" option is only respected`).toHaveBeenWarned()
|
||||
})
|
||||
|
||||
// TODO
|
||||
// it('onTrack', async () => {
|
||||
// const events: DebuggerEvent[] = []
|
||||
// let dummy
|
||||
// const onTrack = vi.fn((e: DebuggerEvent) => {
|
||||
// events.push(e)
|
||||
// })
|
||||
// const obj = reactive({ foo: 1, bar: 2 })
|
||||
// watchEffect(
|
||||
// () => {
|
||||
// dummy = [obj.foo, 'bar' in obj, Object.keys(obj)]
|
||||
// },
|
||||
// { onTrack }
|
||||
// )
|
||||
// await nextTick()
|
||||
// expect(dummy).toEqual([1, true, ['foo', 'bar']])
|
||||
// expect(onTrack).toHaveBeenCalledTimes(3)
|
||||
// expect(events).toMatchObject([
|
||||
// {
|
||||
// target: obj,
|
||||
// type: TrackOpTypes.GET,
|
||||
// key: 'foo'
|
||||
// },
|
||||
// {
|
||||
// target: obj,
|
||||
// type: TrackOpTypes.HAS,
|
||||
// key: 'bar'
|
||||
// },
|
||||
// {
|
||||
// target: obj,
|
||||
// type: TrackOpTypes.ITERATE,
|
||||
// key: ITERATE_KEY
|
||||
// }
|
||||
// ])
|
||||
// })
|
||||
it('onTrack', async () => {
|
||||
const events: DebuggerEvent[] = []
|
||||
let dummy
|
||||
const onTrack = vi.fn((e: DebuggerEvent) => {
|
||||
events.push(e)
|
||||
})
|
||||
const obj = reactive({ foo: 1 })
|
||||
const r = ref(2)
|
||||
const c = computed(() => r.value + 1)
|
||||
// TODO computed & ref
|
||||
watchEffect(
|
||||
() => {
|
||||
dummy = obj.foo + r.value + c.value
|
||||
},
|
||||
{ onTrack }
|
||||
)
|
||||
await nextTick()
|
||||
expect(dummy).toEqual(6)
|
||||
expect(onTrack).toHaveBeenCalledTimes(3)
|
||||
expect(events).toMatchObject([
|
||||
{
|
||||
target: obj,
|
||||
type: TrackOpTypes.GET,
|
||||
key: 'foo'
|
||||
},
|
||||
{
|
||||
target: r,
|
||||
type: TrackOpTypes.GET,
|
||||
key: 'value'
|
||||
},
|
||||
{
|
||||
target: c,
|
||||
type: TrackOpTypes.GET,
|
||||
key: 'value'
|
||||
}
|
||||
])
|
||||
})
|
||||
|
||||
// it('onTrigger', async () => {
|
||||
// const events: DebuggerEvent[] = []
|
||||
|
@ -3,9 +3,9 @@ import {
|
||||
reactive,
|
||||
ref,
|
||||
isReadonly,
|
||||
// toRaw,
|
||||
WritableComputedRef
|
||||
// DebuggerEvent
|
||||
WritableComputedRef,
|
||||
DebuggerEvent,
|
||||
TrackOpTypes
|
||||
} from 'v3'
|
||||
import { effect } from 'v3/reactivity/effect'
|
||||
import { nextTick } from 'core/util'
|
||||
@ -225,39 +225,32 @@ describe('reactivity/computed', () => {
|
||||
expect(x.value).toBe(1)
|
||||
})
|
||||
|
||||
// TODO
|
||||
// it('debug: onTrack', () => {
|
||||
// let events: DebuggerEvent[] = []
|
||||
// const onTrack = vi.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: onTrack', () => {
|
||||
let events: DebuggerEvent[] = []
|
||||
const onTrack = vi.fn((e: DebuggerEvent) => {
|
||||
events.push(e)
|
||||
})
|
||||
const obj = reactive({ foo: 1, bar: 2 })
|
||||
const c = computed(() => obj.foo + obj.bar, {
|
||||
onTrack
|
||||
})
|
||||
expect(c.value).toEqual(3)
|
||||
expect(onTrack).toHaveBeenCalledTimes(2)
|
||||
expect(events).toEqual([
|
||||
{
|
||||
effect: c.effect,
|
||||
target: obj,
|
||||
type: TrackOpTypes.GET,
|
||||
key: 'foo'
|
||||
},
|
||||
{
|
||||
effect: c.effect,
|
||||
target: obj,
|
||||
type: TrackOpTypes.GET,
|
||||
key: 'bar'
|
||||
}
|
||||
])
|
||||
})
|
||||
|
||||
// TODO
|
||||
// it('debug: onTrigger', () => {
|
||||
|
Loading…
Reference in New Issue
Block a user