refactor(reactivity): separate track and trigger operation types
This commit is contained in:
parent
7522d4d61a
commit
89a187b895
@ -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
|
||||||
})
|
})
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
) {
|
) {
|
||||||
|
@ -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'
|
||||||
|
@ -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'
|
||||||
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
)
|
)
|
||||||
|
@ -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
|
||||||
})
|
})
|
||||||
|
@ -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
|
||||||
})
|
})
|
||||||
|
@ -14,7 +14,8 @@ export {
|
|||||||
ReactiveEffect,
|
ReactiveEffect,
|
||||||
ReactiveEffectOptions,
|
ReactiveEffectOptions,
|
||||||
DebuggerEvent,
|
DebuggerEvent,
|
||||||
OperationTypes,
|
TrackOpTypes,
|
||||||
|
TriggerOpTypes,
|
||||||
Ref,
|
Ref,
|
||||||
ComputedRef,
|
ComputedRef,
|
||||||
UnwrapRef,
|
UnwrapRef,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user