refactor(reactivity): separate track and trigger operation types

This commit is contained in:
Evan You 2019-12-03 11:30:24 -05:00
parent 7522d4d61a
commit 89a187b895
11 changed files with 96 additions and 101 deletions

View File

@ -3,7 +3,8 @@ import {
effect, effect,
stop, stop,
toRaw, toRaw,
OperationTypes, TrackOpTypes,
TriggerOpTypes,
DebuggerEvent, DebuggerEvent,
markNonReactive markNonReactive
} from '../src/index' } from '../src/index'
@ -603,19 +604,19 @@ describe('reactivity/effect', () => {
{ {
effect: runner, effect: runner,
target: toRaw(obj), target: toRaw(obj),
type: OperationTypes.GET, type: TrackOpTypes.GET,
key: 'foo' key: 'foo'
}, },
{ {
effect: runner, effect: runner,
target: toRaw(obj), target: toRaw(obj),
type: OperationTypes.HAS, type: TrackOpTypes.HAS,
key: 'bar' key: 'bar'
}, },
{ {
effect: runner, effect: runner,
target: toRaw(obj), target: toRaw(obj),
type: OperationTypes.ITERATE, type: TrackOpTypes.ITERATE,
key: ITERATE_KEY key: ITERATE_KEY
} }
]) ])
@ -641,7 +642,7 @@ describe('reactivity/effect', () => {
expect(events[0]).toEqual({ expect(events[0]).toEqual({
effect: runner, effect: runner,
target: toRaw(obj), target: toRaw(obj),
type: OperationTypes.SET, type: TriggerOpTypes.SET,
key: 'foo', key: 'foo',
oldValue: 1, oldValue: 1,
newValue: 2 newValue: 2
@ -653,7 +654,7 @@ describe('reactivity/effect', () => {
expect(events[1]).toEqual({ expect(events[1]).toEqual({
effect: runner, effect: runner,
target: toRaw(obj), target: toRaw(obj),
type: OperationTypes.DELETE, type: TriggerOpTypes.DELETE,
key: 'foo', key: 'foo',
oldValue: 2 oldValue: 2
}) })

View File

@ -1,6 +1,6 @@
import { reactive, readonly, toRaw } from './reactive' import { reactive, readonly, toRaw } from './reactive'
import { OperationTypes } from './operations' import { TrackOpTypes, TriggerOpTypes } from './operations'
import { track, trigger } from './effect' import { track, trigger, ITERATE_KEY } from './effect'
import { LOCKED } from './lock' import { LOCKED } from './lock'
import { isObject, hasOwn, isSymbol, hasChanged } from '@vue/shared' import { isObject, hasOwn, isSymbol, hasChanged } from '@vue/shared'
import { isRef } from './ref' import { isRef } from './ref'
@ -18,14 +18,14 @@ function createGetter(isReadonly: boolean, shallow = false) {
return res return res
} }
if (shallow) { if (shallow) {
track(target, OperationTypes.GET, key) track(target, TrackOpTypes.GET, key)
// TODO strict mode that returns a shallow-readonly version of the value // TODO strict mode that returns a shallow-readonly version of the value
return res return res
} }
if (isRef(res)) { if (isRef(res)) {
return res.value return res.value
} }
track(target, OperationTypes.GET, key) track(target, TrackOpTypes.GET, key)
return isObject(res) return isObject(res)
? isReadonly ? isReadonly
? // need to lazy access readonly and reactive here to avoid ? // need to lazy access readonly and reactive here to avoid
@ -56,15 +56,15 @@ function set(
if (__DEV__) { if (__DEV__) {
const extraInfo = { oldValue, newValue: value } const extraInfo = { oldValue, newValue: value }
if (!hadKey) { if (!hadKey) {
trigger(target, OperationTypes.ADD, key, extraInfo) trigger(target, TriggerOpTypes.ADD, key, extraInfo)
} else if (hasChanged(value, oldValue)) { } else if (hasChanged(value, oldValue)) {
trigger(target, OperationTypes.SET, key, extraInfo) trigger(target, TriggerOpTypes.SET, key, extraInfo)
} }
} else { } else {
if (!hadKey) { if (!hadKey) {
trigger(target, OperationTypes.ADD, key) trigger(target, TriggerOpTypes.ADD, key)
} else if (hasChanged(value, oldValue)) { } else if (hasChanged(value, oldValue)) {
trigger(target, OperationTypes.SET, key) trigger(target, TriggerOpTypes.SET, key)
} }
} }
} }
@ -78,9 +78,9 @@ function deleteProperty(target: object, key: string | symbol): boolean {
if (result && hadKey) { if (result && hadKey) {
/* istanbul ignore else */ /* istanbul ignore else */
if (__DEV__) { if (__DEV__) {
trigger(target, OperationTypes.DELETE, key, { oldValue }) trigger(target, TriggerOpTypes.DELETE, key, { oldValue })
} else { } else {
trigger(target, OperationTypes.DELETE, key) trigger(target, TriggerOpTypes.DELETE, key)
} }
} }
return result return result
@ -88,12 +88,12 @@ function deleteProperty(target: object, key: string | symbol): boolean {
function has(target: object, key: string | symbol): boolean { function has(target: object, key: string | symbol): boolean {
const result = Reflect.has(target, key) const result = Reflect.has(target, key)
track(target, OperationTypes.HAS, key) track(target, TrackOpTypes.HAS, key)
return result return result
} }
function ownKeys(target: object): (string | number | symbol)[] { function ownKeys(target: object): (string | number | symbol)[] {
track(target, OperationTypes.ITERATE) track(target, TrackOpTypes.ITERATE, ITERATE_KEY)
return Reflect.ownKeys(target) return Reflect.ownKeys(target)
} }

View File

@ -1,6 +1,6 @@
import { toRaw, reactive, readonly } from './reactive' import { toRaw, reactive, readonly } from './reactive'
import { track, trigger } from './effect' import { track, trigger, ITERATE_KEY } from './effect'
import { OperationTypes } from './operations' import { TrackOpTypes, TriggerOpTypes } from './operations'
import { LOCKED } from './lock' import { LOCKED } from './lock'
import { isObject, capitalize, hasOwn, hasChanged } from '@vue/shared' import { isObject, capitalize, hasOwn, hasChanged } from '@vue/shared'
@ -27,20 +27,20 @@ function get(
) { ) {
target = toRaw(target) target = toRaw(target)
key = toRaw(key) key = toRaw(key)
track(target, OperationTypes.GET, key) track(target, TrackOpTypes.GET, key)
return wrap(getProto(target).get.call(target, key)) return wrap(getProto(target).get.call(target, key))
} }
function has(this: CollectionTypes, key: unknown): boolean { function has(this: CollectionTypes, key: unknown): boolean {
const target = toRaw(this) const target = toRaw(this)
key = toRaw(key) key = toRaw(key)
track(target, OperationTypes.HAS, key) track(target, TrackOpTypes.HAS, key)
return getProto(target).has.call(target, key) return getProto(target).has.call(target, key)
} }
function size(target: IterableCollections) { function size(target: IterableCollections) {
target = toRaw(target) target = toRaw(target)
track(target, OperationTypes.ITERATE) track(target, TrackOpTypes.ITERATE, ITERATE_KEY)
return Reflect.get(getProto(target), 'size', target) return Reflect.get(getProto(target), 'size', target)
} }
@ -53,9 +53,9 @@ function add(this: SetTypes, value: unknown) {
if (!hadKey) { if (!hadKey) {
/* istanbul ignore else */ /* istanbul ignore else */
if (__DEV__) { if (__DEV__) {
trigger(target, OperationTypes.ADD, value, { newValue: value }) trigger(target, TriggerOpTypes.ADD, value, { newValue: value })
} else { } else {
trigger(target, OperationTypes.ADD, value) trigger(target, TriggerOpTypes.ADD, value)
} }
} }
return result return result
@ -72,15 +72,15 @@ function set(this: MapTypes, key: unknown, value: unknown) {
if (__DEV__) { if (__DEV__) {
const extraInfo = { oldValue, newValue: value } const extraInfo = { oldValue, newValue: value }
if (!hadKey) { if (!hadKey) {
trigger(target, OperationTypes.ADD, key, extraInfo) trigger(target, TriggerOpTypes.ADD, key, extraInfo)
} else if (hasChanged(value, oldValue)) { } else if (hasChanged(value, oldValue)) {
trigger(target, OperationTypes.SET, key, extraInfo) trigger(target, TriggerOpTypes.SET, key, extraInfo)
} }
} else { } else {
if (!hadKey) { if (!hadKey) {
trigger(target, OperationTypes.ADD, key) trigger(target, TriggerOpTypes.ADD, key)
} else if (hasChanged(value, oldValue)) { } else if (hasChanged(value, oldValue)) {
trigger(target, OperationTypes.SET, key) trigger(target, TriggerOpTypes.SET, key)
} }
} }
return result return result
@ -96,9 +96,9 @@ function deleteEntry(this: CollectionTypes, key: unknown) {
if (hadKey) { if (hadKey) {
/* istanbul ignore else */ /* istanbul ignore else */
if (__DEV__) { if (__DEV__) {
trigger(target, OperationTypes.DELETE, key, { oldValue }) trigger(target, TriggerOpTypes.DELETE, key, { oldValue })
} else { } else {
trigger(target, OperationTypes.DELETE, key) trigger(target, TriggerOpTypes.DELETE, key)
} }
} }
return result return result
@ -117,9 +117,9 @@ function clear(this: IterableCollections) {
if (hadItems) { if (hadItems) {
/* istanbul ignore else */ /* istanbul ignore else */
if (__DEV__) { if (__DEV__) {
trigger(target, OperationTypes.CLEAR, void 0, { oldTarget }) trigger(target, TriggerOpTypes.CLEAR, void 0, { oldTarget })
} else { } else {
trigger(target, OperationTypes.CLEAR) trigger(target, TriggerOpTypes.CLEAR)
} }
} }
return result return result
@ -134,7 +134,7 @@ function createForEach(isReadonly: boolean) {
const observed = this const observed = this
const target = toRaw(observed) const target = toRaw(observed)
const wrap = isReadonly ? toReadonly : toReactive const wrap = isReadonly ? toReadonly : toReactive
track(target, OperationTypes.ITERATE) track(target, TrackOpTypes.ITERATE, ITERATE_KEY)
// important: create sure the callback is // important: create sure the callback is
// 1. invoked with the reactive map as `this` and 3rd arg // 1. invoked with the reactive map as `this` and 3rd arg
// 2. the value received should be a corresponding reactive/readonly. // 2. the value received should be a corresponding reactive/readonly.
@ -153,7 +153,7 @@ function createIterableMethod(method: string | symbol, isReadonly: boolean) {
(method === Symbol.iterator && target instanceof Map) (method === Symbol.iterator && target instanceof Map)
const innerIterator = getProto(target)[method].apply(target, args) const innerIterator = getProto(target)[method].apply(target, args)
const wrap = isReadonly ? toReadonly : toReactive 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 // return a wrapped iterator which returns observed versions of the
// values emitted from the real iterator // values emitted from the real iterator
return { return {
@ -177,7 +177,7 @@ function createIterableMethod(method: string | symbol, isReadonly: boolean) {
function createReadonlyMethod( function createReadonlyMethod(
method: Function, method: Function,
type: OperationTypes type: TriggerOpTypes
): Function { ): Function {
return function(this: CollectionTypes, ...args: unknown[]) { return function(this: CollectionTypes, ...args: unknown[]) {
if (LOCKED) { if (LOCKED) {
@ -188,7 +188,7 @@ function createReadonlyMethod(
toRaw(this) toRaw(this)
) )
} }
return type === OperationTypes.DELETE ? false : this return type === TriggerOpTypes.DELETE ? false : this
} else { } else {
return method.apply(this, args) return method.apply(this, args)
} }
@ -218,10 +218,10 @@ const readonlyInstrumentations: Record<string, Function> = {
return size(this) return size(this)
}, },
has, has,
add: createReadonlyMethod(add, OperationTypes.ADD), add: createReadonlyMethod(add, TriggerOpTypes.ADD),
set: createReadonlyMethod(set, OperationTypes.SET), set: createReadonlyMethod(set, TriggerOpTypes.SET),
delete: createReadonlyMethod(deleteEntry, OperationTypes.DELETE), delete: createReadonlyMethod(deleteEntry, TriggerOpTypes.DELETE),
clear: createReadonlyMethod(clear, OperationTypes.CLEAR), clear: createReadonlyMethod(clear, TriggerOpTypes.CLEAR),
forEach: createForEach(true) forEach: createForEach(true)
} }

View File

@ -1,7 +1,14 @@
import { OperationTypes } from './operations' import { TrackOpTypes, TriggerOpTypes } from './operations'
import { Dep, targetMap } from './reactive'
import { EMPTY_OBJ, extend, isArray } from '@vue/shared' 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> { export interface ReactiveEffect<T = any> {
(): T (): T
_isEffect: true _isEffect: true
@ -23,7 +30,7 @@ export interface ReactiveEffectOptions {
export type DebuggerEvent = { export type DebuggerEvent = {
effect: ReactiveEffect effect: ReactiveEffect
target: object target: object
type: OperationTypes type: TrackOpTypes | TriggerOpTypes
key: any key: any
} & DebuggerEventExtraInfo } & DebuggerEventExtraInfo
@ -115,21 +122,18 @@ export function resumeTracking() {
shouldTrack = true 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) { if (!shouldTrack || effectStack.length === 0) {
return return
} }
const effect = effectStack[effectStack.length - 1] const effect = effectStack[effectStack.length - 1]
if (type === OperationTypes.ITERATE) {
key = ITERATE_KEY
}
let depsMap = targetMap.get(target) let depsMap = targetMap.get(target)
if (depsMap === void 0) { if (depsMap === void 0) {
targetMap.set(target, (depsMap = new Map())) targetMap.set(target, (depsMap = new Map()))
} }
let dep = depsMap.get(key!) let dep = depsMap.get(key)
if (dep === void 0) { if (dep === void 0) {
depsMap.set(key!, (dep = new Set())) depsMap.set(key, (dep = new Set()))
} }
if (!dep.has(effect)) { if (!dep.has(effect)) {
dep.add(effect) dep.add(effect)
@ -147,7 +151,7 @@ export function track(target: object, type: OperationTypes, key?: unknown) {
export function trigger( export function trigger(
target: object, target: object,
type: OperationTypes, type: TriggerOpTypes,
key?: unknown, key?: unknown,
extraInfo?: DebuggerEventExtraInfo extraInfo?: DebuggerEventExtraInfo
) { ) {
@ -158,7 +162,7 @@ export function trigger(
} }
const effects = new Set<ReactiveEffect>() const effects = new Set<ReactiveEffect>()
const computedRunners = new Set<ReactiveEffect>() const computedRunners = new Set<ReactiveEffect>()
if (type === OperationTypes.CLEAR) { if (type === TriggerOpTypes.CLEAR) {
// collection being cleared, trigger all effects for target // collection being cleared, trigger all effects for target
depsMap.forEach(dep => { depsMap.forEach(dep => {
addRunners(effects, computedRunners, dep) addRunners(effects, computedRunners, dep)
@ -169,7 +173,7 @@ export function trigger(
addRunners(effects, computedRunners, depsMap.get(key)) addRunners(effects, computedRunners, depsMap.get(key))
} }
// also run for iteration key on ADD | DELETE // 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 const iterationKey = isArray(target) ? 'length' : ITERATE_KEY
addRunners(effects, computedRunners, depsMap.get(iterationKey)) addRunners(effects, computedRunners, depsMap.get(iterationKey))
} }
@ -202,7 +206,7 @@ function addRunners(
function scheduleRun( function scheduleRun(
effect: ReactiveEffect, effect: ReactiveEffect,
target: object, target: object,
type: OperationTypes, type: TriggerOpTypes,
key: unknown, key: unknown,
extraInfo?: DebuggerEventExtraInfo extraInfo?: DebuggerEventExtraInfo
) { ) {

View File

@ -28,4 +28,4 @@ export {
DebuggerEvent DebuggerEvent
} from './effect' } from './effect'
export { lock, unlock } from './lock' export { lock, unlock } from './lock'
export { OperationTypes } from './operations' export { TrackOpTypes, TriggerOpTypes } from './operations'

View File

@ -1,11 +1,15 @@
export const enum OperationTypes {
// using literal strings instead of numbers so that it's easier to inspect // using literal strings instead of numbers so that it's easier to inspect
// debugger events // debugger events
SET = 'set',
ADD = 'add', export const enum TrackOpTypes {
DELETE = 'delete',
CLEAR = 'clear',
GET = 'get', GET = 'get',
HAS = 'has', HAS = 'has',
ITERATE = 'iterate' ITERATE = 'iterate'
} }
export const enum TriggerOpTypes {
SET = 'set',
ADD = 'add',
DELETE = 'delete',
CLEAR = 'clear'
}

View File

@ -8,18 +8,9 @@ import {
mutableCollectionHandlers, mutableCollectionHandlers,
readonlyCollectionHandlers readonlyCollectionHandlers
} from './collectionHandlers' } from './collectionHandlers'
import { ReactiveEffect } from './effect'
import { UnwrapRef, Ref } from './ref' import { UnwrapRef, Ref } from './ref'
import { makeMap } from '@vue/shared' 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. // WeakMaps that store {raw <-> observed} pairs.
const rawToReactive = new WeakMap<any, any>() const rawToReactive = new WeakMap<any, any>()
const reactiveToRaw = new WeakMap<any, any>() const reactiveToRaw = new WeakMap<any, any>()
@ -132,9 +123,6 @@ function createReactiveObject(
observed = new Proxy(target, handlers) observed = new Proxy(target, handlers)
toProxy.set(target, observed) toProxy.set(target, observed)
toRaw.set(observed, target) toRaw.set(observed, target)
if (!targetMap.has(target)) {
targetMap.set(target, new Map())
}
return observed return observed
} }

View File

@ -1,5 +1,5 @@
import { track, trigger } from './effect' import { track, trigger } from './effect'
import { OperationTypes } from './operations' import { TrackOpTypes, TriggerOpTypes } from './operations'
import { isObject } from '@vue/shared' import { isObject } from '@vue/shared'
import { reactive, isReactive } from './reactive' import { reactive, isReactive } from './reactive'
import { ComputedRef } from './computed' import { ComputedRef } from './computed'
@ -38,14 +38,14 @@ export function ref(raw?: unknown) {
const r = { const r = {
_isRef: true, _isRef: true,
get value() { get value() {
track(r, OperationTypes.GET, 'value') track(r, TrackOpTypes.GET, 'value')
return raw return raw
}, },
set value(newVal) { set value(newVal) {
raw = convert(newVal) raw = convert(newVal)
trigger( trigger(
r, r,
OperationTypes.SET, TriggerOpTypes.SET,
'value', 'value',
__DEV__ ? { newValue: newVal } : void 0 __DEV__ ? { newValue: newVal } : void 0
) )

View File

@ -13,10 +13,10 @@ import {
onUnmounted, onUnmounted,
onRenderTracked, onRenderTracked,
reactive, reactive,
OperationTypes, TrackOpTypes,
onRenderTriggered onRenderTriggered
} from '@vue/runtime-test' } 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 // reference: https://vue-composition-api-rfc.netlify.com/api.html#lifecycle-hooks
@ -283,17 +283,17 @@ describe('api: lifecycle hooks', () => {
expect(events).toMatchObject([ expect(events).toMatchObject([
{ {
target: obj, target: obj,
type: OperationTypes.GET, type: TrackOpTypes.GET,
key: 'foo' key: 'foo'
}, },
{ {
target: obj, target: obj,
type: OperationTypes.HAS, type: TrackOpTypes.HAS,
key: 'bar' key: 'bar'
}, },
{ {
target: obj, target: obj,
type: OperationTypes.ITERATE, type: TrackOpTypes.ITERATE,
key: ITERATE_KEY key: ITERATE_KEY
} }
]) ])
@ -320,7 +320,7 @@ describe('api: lifecycle hooks', () => {
await nextTick() await nextTick()
expect(onTrigger).toHaveBeenCalledTimes(1) expect(onTrigger).toHaveBeenCalledTimes(1)
expect(events[0]).toMatchObject({ expect(events[0]).toMatchObject({
type: OperationTypes.SET, type: TriggerOpTypes.SET,
key: 'foo', key: 'foo',
oldValue: 1, oldValue: 1,
newValue: 2 newValue: 2
@ -330,7 +330,7 @@ describe('api: lifecycle hooks', () => {
await nextTick() await nextTick()
expect(onTrigger).toHaveBeenCalledTimes(2) expect(onTrigger).toHaveBeenCalledTimes(2)
expect(events[1]).toMatchObject({ expect(events[1]).toMatchObject({
type: OperationTypes.DELETE, type: TriggerOpTypes.DELETE,
key: 'bar', key: 'bar',
oldValue: 2 oldValue: 2
}) })
@ -338,7 +338,7 @@ describe('api: lifecycle hooks', () => {
await nextTick() await nextTick()
expect(onTrigger).toHaveBeenCalledTimes(3) expect(onTrigger).toHaveBeenCalledTimes(3)
expect(events[2]).toMatchObject({ expect(events[2]).toMatchObject({
type: OperationTypes.ADD, type: TriggerOpTypes.ADD,
key: 'baz', key: 'baz',
newValue: 3 newValue: 3
}) })

View File

@ -1,14 +1,11 @@
import { import { watch, reactive, computed, nextTick, ref, h } from '../src/index'
watch,
reactive,
computed,
nextTick,
ref,
h,
OperationTypes
} from '../src/index'
import { render, nodeOps, serializeInner } from '@vue/runtime-test' 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 // reference: https://vue-composition-api-rfc.netlify.com/api.html#watch
@ -366,17 +363,17 @@ describe('api: watch', () => {
expect(events).toMatchObject([ expect(events).toMatchObject([
{ {
target: obj, target: obj,
type: OperationTypes.GET, type: TrackOpTypes.GET,
key: 'foo' key: 'foo'
}, },
{ {
target: obj, target: obj,
type: OperationTypes.HAS, type: TrackOpTypes.HAS,
key: 'bar' key: 'bar'
}, },
{ {
target: obj, target: obj,
type: OperationTypes.ITERATE, type: TrackOpTypes.ITERATE,
key: ITERATE_KEY key: ITERATE_KEY
} }
]) ])
@ -403,7 +400,7 @@ describe('api: watch', () => {
expect(dummy).toBe(2) expect(dummy).toBe(2)
expect(onTrigger).toHaveBeenCalledTimes(1) expect(onTrigger).toHaveBeenCalledTimes(1)
expect(events[0]).toMatchObject({ expect(events[0]).toMatchObject({
type: OperationTypes.SET, type: TriggerOpTypes.SET,
key: 'foo', key: 'foo',
oldValue: 1, oldValue: 1,
newValue: 2 newValue: 2
@ -414,7 +411,7 @@ describe('api: watch', () => {
expect(dummy).toBeUndefined() expect(dummy).toBeUndefined()
expect(onTrigger).toHaveBeenCalledTimes(2) expect(onTrigger).toHaveBeenCalledTimes(2)
expect(events[1]).toMatchObject({ expect(events[1]).toMatchObject({
type: OperationTypes.DELETE, type: TriggerOpTypes.DELETE,
key: 'foo', key: 'foo',
oldValue: 2 oldValue: 2
}) })

View File

@ -14,7 +14,8 @@ export {
ReactiveEffect, ReactiveEffect,
ReactiveEffectOptions, ReactiveEffectOptions,
DebuggerEvent, DebuggerEvent,
OperationTypes, TrackOpTypes,
TriggerOpTypes,
Ref, Ref,
ComputedRef, ComputedRef,
UnwrapRef, UnwrapRef,