effect,
stop,
toRaw,
- OperationTypes,
+ TrackOpTypes,
+ TriggerOpTypes,
DebuggerEvent,
markNonReactive
} from '../src/index'
{
effect: runner,
target: toRaw(obj),
- type: OperationTypes.GET,
+ type: TrackOpTypes.GET,
key: 'foo'
},
{
effect: runner,
target: toRaw(obj),
- type: OperationTypes.HAS,
+ type: TrackOpTypes.HAS,
key: 'bar'
},
{
effect: runner,
target: toRaw(obj),
- type: OperationTypes.ITERATE,
+ type: TrackOpTypes.ITERATE,
key: ITERATE_KEY
}
])
expect(events[0]).toEqual({
effect: runner,
target: toRaw(obj),
- type: OperationTypes.SET,
+ type: TriggerOpTypes.SET,
key: 'foo',
oldValue: 1,
newValue: 2
expect(events[1]).toEqual({
effect: runner,
target: toRaw(obj),
- type: OperationTypes.DELETE,
+ type: TriggerOpTypes.DELETE,
key: 'foo',
oldValue: 2
})
import { reactive, readonly, toRaw } from './reactive'
-import { OperationTypes } from './operations'
-import { track, trigger } from './effect'
+import { TrackOpTypes, TriggerOpTypes } from './operations'
+import { track, trigger, ITERATE_KEY } from './effect'
import { LOCKED } from './lock'
import { isObject, hasOwn, isSymbol, hasChanged } from '@vue/shared'
import { isRef } from './ref'
return res
}
if (shallow) {
- track(target, OperationTypes.GET, key)
+ track(target, TrackOpTypes.GET, key)
// TODO strict mode that returns a shallow-readonly version of the value
return res
}
if (isRef(res)) {
return res.value
}
- track(target, OperationTypes.GET, key)
+ track(target, TrackOpTypes.GET, key)
return isObject(res)
? isReadonly
? // need to lazy access readonly and reactive here to avoid
if (__DEV__) {
const extraInfo = { oldValue, newValue: value }
if (!hadKey) {
- trigger(target, OperationTypes.ADD, key, extraInfo)
+ trigger(target, TriggerOpTypes.ADD, key, extraInfo)
} else if (hasChanged(value, oldValue)) {
- trigger(target, OperationTypes.SET, key, extraInfo)
+ trigger(target, TriggerOpTypes.SET, key, extraInfo)
}
} else {
if (!hadKey) {
- trigger(target, OperationTypes.ADD, key)
+ trigger(target, TriggerOpTypes.ADD, key)
} else if (hasChanged(value, oldValue)) {
- trigger(target, OperationTypes.SET, key)
+ trigger(target, TriggerOpTypes.SET, key)
}
}
}
if (result && hadKey) {
/* istanbul ignore else */
if (__DEV__) {
- trigger(target, OperationTypes.DELETE, key, { oldValue })
+ trigger(target, TriggerOpTypes.DELETE, key, { oldValue })
} else {
- trigger(target, OperationTypes.DELETE, key)
+ trigger(target, TriggerOpTypes.DELETE, key)
}
}
return result
function has(target: object, key: string | symbol): boolean {
const result = Reflect.has(target, key)
- track(target, OperationTypes.HAS, key)
+ track(target, TrackOpTypes.HAS, key)
return result
}
function ownKeys(target: object): (string | number | symbol)[] {
- track(target, OperationTypes.ITERATE)
+ track(target, TrackOpTypes.ITERATE, ITERATE_KEY)
return Reflect.ownKeys(target)
}
import { toRaw, reactive, readonly } from './reactive'
-import { track, trigger } from './effect'
-import { OperationTypes } from './operations'
+import { track, trigger, ITERATE_KEY } from './effect'
+import { TrackOpTypes, TriggerOpTypes } from './operations'
import { LOCKED } from './lock'
import { isObject, capitalize, hasOwn, hasChanged } from '@vue/shared'
) {
target = toRaw(target)
key = toRaw(key)
- track(target, OperationTypes.GET, key)
+ track(target, TrackOpTypes.GET, key)
return wrap(getProto(target).get.call(target, key))
}
function has(this: CollectionTypes, key: unknown): boolean {
const target = toRaw(this)
key = toRaw(key)
- track(target, OperationTypes.HAS, key)
+ track(target, TrackOpTypes.HAS, key)
return getProto(target).has.call(target, key)
}
function size(target: IterableCollections) {
target = toRaw(target)
- track(target, OperationTypes.ITERATE)
+ track(target, TrackOpTypes.ITERATE, ITERATE_KEY)
return Reflect.get(getProto(target), 'size', target)
}
if (!hadKey) {
/* istanbul ignore else */
if (__DEV__) {
- trigger(target, OperationTypes.ADD, value, { newValue: value })
+ trigger(target, TriggerOpTypes.ADD, value, { newValue: value })
} else {
- trigger(target, OperationTypes.ADD, value)
+ trigger(target, TriggerOpTypes.ADD, value)
}
}
return result
if (__DEV__) {
const extraInfo = { oldValue, newValue: value }
if (!hadKey) {
- trigger(target, OperationTypes.ADD, key, extraInfo)
+ trigger(target, TriggerOpTypes.ADD, key, extraInfo)
} else if (hasChanged(value, oldValue)) {
- trigger(target, OperationTypes.SET, key, extraInfo)
+ trigger(target, TriggerOpTypes.SET, key, extraInfo)
}
} else {
if (!hadKey) {
- trigger(target, OperationTypes.ADD, key)
+ trigger(target, TriggerOpTypes.ADD, key)
} else if (hasChanged(value, oldValue)) {
- trigger(target, OperationTypes.SET, key)
+ trigger(target, TriggerOpTypes.SET, key)
}
}
return result
if (hadKey) {
/* istanbul ignore else */
if (__DEV__) {
- trigger(target, OperationTypes.DELETE, key, { oldValue })
+ trigger(target, TriggerOpTypes.DELETE, key, { oldValue })
} else {
- trigger(target, OperationTypes.DELETE, key)
+ trigger(target, TriggerOpTypes.DELETE, key)
}
}
return result
if (hadItems) {
/* istanbul ignore else */
if (__DEV__) {
- trigger(target, OperationTypes.CLEAR, void 0, { oldTarget })
+ trigger(target, TriggerOpTypes.CLEAR, void 0, { oldTarget })
} else {
- trigger(target, OperationTypes.CLEAR)
+ trigger(target, TriggerOpTypes.CLEAR)
}
}
return result
const observed = this
const target = toRaw(observed)
const wrap = isReadonly ? toReadonly : toReactive
- track(target, OperationTypes.ITERATE)
+ track(target, TrackOpTypes.ITERATE, ITERATE_KEY)
// important: create sure the callback is
// 1. invoked with the reactive map as `this` and 3rd arg
// 2. the value received should be a corresponding reactive/readonly.
(method === Symbol.iterator && target instanceof Map)
const innerIterator = getProto(target)[method].apply(target, args)
const wrap = isReadonly ? toReadonly : toReactive
- track(target, OperationTypes.ITERATE)
+ track(target, TrackOpTypes.ITERATE, ITERATE_KEY)
// return a wrapped iterator which returns observed versions of the
// values emitted from the real iterator
return {
function createReadonlyMethod(
method: Function,
- type: OperationTypes
+ type: TriggerOpTypes
): Function {
return function(this: CollectionTypes, ...args: unknown[]) {
if (LOCKED) {
toRaw(this)
)
}
- return type === OperationTypes.DELETE ? false : this
+ return type === TriggerOpTypes.DELETE ? false : this
} else {
return method.apply(this, args)
}
return size(this)
},
has,
- add: createReadonlyMethod(add, OperationTypes.ADD),
- set: createReadonlyMethod(set, OperationTypes.SET),
- delete: createReadonlyMethod(deleteEntry, OperationTypes.DELETE),
- clear: createReadonlyMethod(clear, OperationTypes.CLEAR),
+ add: createReadonlyMethod(add, TriggerOpTypes.ADD),
+ set: createReadonlyMethod(set, TriggerOpTypes.SET),
+ delete: createReadonlyMethod(deleteEntry, TriggerOpTypes.DELETE),
+ clear: createReadonlyMethod(clear, TriggerOpTypes.CLEAR),
forEach: createForEach(true)
}
-import { OperationTypes } from './operations'
-import { Dep, targetMap } from './reactive'
+import { TrackOpTypes, TriggerOpTypes } from './operations'
import { EMPTY_OBJ, extend, isArray } from '@vue/shared'
+// The main WeakMap that stores {target -> key -> dep} connections.
+// Conceptually, it's easier to think of a dependency as a Dep class
+// which maintains a Set of subscribers, but we simply store them as
+// raw Sets to reduce memory overhead.
+type Dep = Set<ReactiveEffect>
+type KeyToDepMap = Map<any, Dep>
+const targetMap = new WeakMap<any, KeyToDepMap>()
+
export interface ReactiveEffect<T = any> {
(): T
_isEffect: true
export type DebuggerEvent = {
effect: ReactiveEffect
target: object
- type: OperationTypes
+ type: TrackOpTypes | TriggerOpTypes
key: any
} & DebuggerEventExtraInfo
shouldTrack = true
}
-export function track(target: object, type: OperationTypes, key?: unknown) {
+export function track(target: object, type: TrackOpTypes, key: unknown) {
if (!shouldTrack || effectStack.length === 0) {
return
}
const effect = effectStack[effectStack.length - 1]
- if (type === OperationTypes.ITERATE) {
- key = ITERATE_KEY
- }
let depsMap = targetMap.get(target)
if (depsMap === void 0) {
targetMap.set(target, (depsMap = new Map()))
}
- let dep = depsMap.get(key!)
+ let dep = depsMap.get(key)
if (dep === void 0) {
- depsMap.set(key!, (dep = new Set()))
+ depsMap.set(key, (dep = new Set()))
}
if (!dep.has(effect)) {
dep.add(effect)
export function trigger(
target: object,
- type: OperationTypes,
+ type: TriggerOpTypes,
key?: unknown,
extraInfo?: DebuggerEventExtraInfo
) {
}
const effects = new Set<ReactiveEffect>()
const computedRunners = new Set<ReactiveEffect>()
- if (type === OperationTypes.CLEAR) {
+ if (type === TriggerOpTypes.CLEAR) {
// collection being cleared, trigger all effects for target
depsMap.forEach(dep => {
addRunners(effects, computedRunners, dep)
addRunners(effects, computedRunners, depsMap.get(key))
}
// also run for iteration key on ADD | DELETE
- if (type === OperationTypes.ADD || type === OperationTypes.DELETE) {
+ if (type === TriggerOpTypes.ADD || type === TriggerOpTypes.DELETE) {
const iterationKey = isArray(target) ? 'length' : ITERATE_KEY
addRunners(effects, computedRunners, depsMap.get(iterationKey))
}
function scheduleRun(
effect: ReactiveEffect,
target: object,
- type: OperationTypes,
+ type: TriggerOpTypes,
key: unknown,
extraInfo?: DebuggerEventExtraInfo
) {
DebuggerEvent
} from './effect'
export { lock, unlock } from './lock'
-export { OperationTypes } from './operations'
+export { TrackOpTypes, TriggerOpTypes } from './operations'
-export const enum OperationTypes {
- // using literal strings instead of numbers so that it's easier to inspect
- // debugger events
- SET = 'set',
- ADD = 'add',
- DELETE = 'delete',
- CLEAR = 'clear',
+// using literal strings instead of numbers so that it's easier to inspect
+// debugger events
+
+export const enum TrackOpTypes {
GET = 'get',
HAS = 'has',
ITERATE = 'iterate'
}
+
+export const enum TriggerOpTypes {
+ SET = 'set',
+ ADD = 'add',
+ DELETE = 'delete',
+ CLEAR = 'clear'
+}
mutableCollectionHandlers,
readonlyCollectionHandlers
} from './collectionHandlers'
-import { ReactiveEffect } from './effect'
import { UnwrapRef, Ref } from './ref'
import { makeMap } from '@vue/shared'
-// The main WeakMap that stores {target -> key -> dep} connections.
-// Conceptually, it's easier to think of a dependency as a Dep class
-// which maintains a Set of subscribers, but we simply store them as
-// raw Sets to reduce memory overhead.
-export type Dep = Set<ReactiveEffect>
-export type KeyToDepMap = Map<any, Dep>
-export const targetMap = new WeakMap<any, KeyToDepMap>()
-
// WeakMaps that store {raw <-> observed} pairs.
const rawToReactive = new WeakMap<any, any>()
const reactiveToRaw = new WeakMap<any, any>()
observed = new Proxy(target, handlers)
toProxy.set(target, observed)
toRaw.set(observed, target)
- if (!targetMap.has(target)) {
- targetMap.set(target, new Map())
- }
return observed
}
import { track, trigger } from './effect'
-import { OperationTypes } from './operations'
+import { TrackOpTypes, TriggerOpTypes } from './operations'
import { isObject } from '@vue/shared'
import { reactive, isReactive } from './reactive'
import { ComputedRef } from './computed'
const r = {
_isRef: true,
get value() {
- track(r, OperationTypes.GET, 'value')
+ track(r, TrackOpTypes.GET, 'value')
return raw
},
set value(newVal) {
raw = convert(newVal)
trigger(
r,
- OperationTypes.SET,
+ TriggerOpTypes.SET,
'value',
__DEV__ ? { newValue: newVal } : void 0
)
onUnmounted,
onRenderTracked,
reactive,
- OperationTypes,
+ TrackOpTypes,
onRenderTriggered
} from '@vue/runtime-test'
-import { ITERATE_KEY, DebuggerEvent } from '@vue/reactivity'
+import { ITERATE_KEY, DebuggerEvent, TriggerOpTypes } from '@vue/reactivity'
// reference: https://vue-composition-api-rfc.netlify.com/api.html#lifecycle-hooks
expect(events).toMatchObject([
{
target: obj,
- type: OperationTypes.GET,
+ type: TrackOpTypes.GET,
key: 'foo'
},
{
target: obj,
- type: OperationTypes.HAS,
+ type: TrackOpTypes.HAS,
key: 'bar'
},
{
target: obj,
- type: OperationTypes.ITERATE,
+ type: TrackOpTypes.ITERATE,
key: ITERATE_KEY
}
])
await nextTick()
expect(onTrigger).toHaveBeenCalledTimes(1)
expect(events[0]).toMatchObject({
- type: OperationTypes.SET,
+ type: TriggerOpTypes.SET,
key: 'foo',
oldValue: 1,
newValue: 2
await nextTick()
expect(onTrigger).toHaveBeenCalledTimes(2)
expect(events[1]).toMatchObject({
- type: OperationTypes.DELETE,
+ type: TriggerOpTypes.DELETE,
key: 'bar',
oldValue: 2
})
await nextTick()
expect(onTrigger).toHaveBeenCalledTimes(3)
expect(events[2]).toMatchObject({
- type: OperationTypes.ADD,
+ type: TriggerOpTypes.ADD,
key: 'baz',
newValue: 3
})
-import {
- watch,
- reactive,
- computed,
- nextTick,
- ref,
- h,
- OperationTypes
-} from '../src/index'
+import { watch, reactive, computed, nextTick, ref, h } from '../src/index'
import { render, nodeOps, serializeInner } from '@vue/runtime-test'
-import { ITERATE_KEY, DebuggerEvent } from '@vue/reactivity'
+import {
+ ITERATE_KEY,
+ DebuggerEvent,
+ TrackOpTypes,
+ TriggerOpTypes
+} from '@vue/reactivity'
// reference: https://vue-composition-api-rfc.netlify.com/api.html#watch
expect(events).toMatchObject([
{
target: obj,
- type: OperationTypes.GET,
+ type: TrackOpTypes.GET,
key: 'foo'
},
{
target: obj,
- type: OperationTypes.HAS,
+ type: TrackOpTypes.HAS,
key: 'bar'
},
{
target: obj,
- type: OperationTypes.ITERATE,
+ type: TrackOpTypes.ITERATE,
key: ITERATE_KEY
}
])
expect(dummy).toBe(2)
expect(onTrigger).toHaveBeenCalledTimes(1)
expect(events[0]).toMatchObject({
- type: OperationTypes.SET,
+ type: TriggerOpTypes.SET,
key: 'foo',
oldValue: 1,
newValue: 2
expect(dummy).toBeUndefined()
expect(onTrigger).toHaveBeenCalledTimes(2)
expect(events[1]).toMatchObject({
- type: OperationTypes.DELETE,
+ type: TriggerOpTypes.DELETE,
key: 'foo',
oldValue: 2
})
ReactiveEffect,
ReactiveEffectOptions,
DebuggerEvent,
- OperationTypes,
+ TrackOpTypes,
+ TriggerOpTypes,
Ref,
ComputedRef,
UnwrapRef,