diff --git a/packages/observer/src/baseHandlers.ts b/packages/observer/src/baseHandlers.ts index 62515469..40f57fd8 100644 --- a/packages/observer/src/baseHandlers.ts +++ b/packages/observer/src/baseHandlers.ts @@ -3,6 +3,7 @@ import { OperationTypes } from './operations' import { track, trigger } from './effect' import { LOCKED } from './lock' import { isObject } from '@vue/shared' +import { isValue } from './value' const hasOwnProperty = Object.prototype.hasOwnProperty @@ -18,6 +19,9 @@ function createGetter(isImmutable: boolean) { if (typeof key === 'symbol' && builtInSymbols.has(key)) { return res } + if (isValue(res)) { + return res.value + } track(target, OperationTypes.GET, key) return isObject(res) ? isImmutable @@ -38,6 +42,10 @@ function set( value = unwrap(value) const hadKey = hasOwnProperty.call(target, key) const oldValue = target[key] + if (isValue(oldValue)) { + oldValue.value = value + return true + } const result = Reflect.set(target, key, value, receiver) // don't trigger if target is something up in the prototype chain of original if (target === unwrap(receiver)) { diff --git a/packages/observer/src/effect.ts b/packages/observer/src/effect.ts index e4ae3d92..e149de04 100644 --- a/packages/observer/src/effect.ts +++ b/packages/observer/src/effect.ts @@ -1,5 +1,5 @@ import { OperationTypes } from './operations' -import { Dep, KeyToDepMap, targetMap } from './state' +import { Dep, targetMap } from './state' export interface ReactiveEffect { (): any @@ -84,8 +84,10 @@ export function track( if (type === OperationTypes.ITERATE) { key = ITERATE_KEY } - // keyMap must exist because only an observed target can call this function - const depsMap = targetMap.get(target) as KeyToDepMap + let depsMap = targetMap.get(target) + if (depsMap === void 0) { + targetMap.set(target, (depsMap = new Map())) + } let dep = depsMap.get(key as string | symbol) if (!dep) { depsMap.set(key as string | symbol, (dep = new Set())) @@ -111,7 +113,11 @@ export function trigger( key?: string | symbol, extraInfo?: any ) { - const depsMap = targetMap.get(target) as KeyToDepMap + const depsMap = targetMap.get(target) + if (depsMap === void 0) { + // never been tracked + return + } const effects = new Set() const computedRunners = new Set() if (type === OperationTypes.CLEAR) { diff --git a/packages/observer/src/index.ts b/packages/observer/src/index.ts index a2cdf806..35b13ec3 100644 --- a/packages/observer/src/index.ts +++ b/packages/observer/src/index.ts @@ -28,6 +28,7 @@ export { ReactiveEffect, ReactiveEffectOptions, DebuggerEvent } export { OperationTypes } from './operations' export { computed, ComputedGetter } from './computed' export { lock, unlock } from './lock' +export { value, isValue } from './value' const collectionTypes: Set = new Set([Set, Map, WeakMap, WeakSet]) const observableValueRE = /^\[object (?:Object|Array|Map|Set|WeakMap|WeakSet)\]$/ diff --git a/packages/observer/src/value.ts b/packages/observer/src/value.ts new file mode 100644 index 00000000..b37e100c --- /dev/null +++ b/packages/observer/src/value.ts @@ -0,0 +1,27 @@ +import { track, trigger } from './effect' +import { OperationTypes } from './operations' + +const knownValues = new WeakSet() + +export interface Value { + value: T +} + +export function value(raw: T): Value { + const v = { + get value() { + track(v, OperationTypes.GET, '') + return raw + }, + set value(newVal) { + raw = newVal + trigger(v, OperationTypes.SET, '') + } + } + knownValues.add(v) + return v +} + +export function isValue(v: any): boolean { + return knownValues.has(v) +} diff --git a/packages/runtime-core/src/componentProxy.ts b/packages/runtime-core/src/componentProxy.ts index 0974e6cb..0b11228f 100644 --- a/packages/runtime-core/src/componentProxy.ts +++ b/packages/runtime-core/src/componentProxy.ts @@ -1,17 +1,10 @@ import { ComponentInstance } from './component' -import { isObservable, unwrap } from '@vue/observer' - -// TODO use proper implementation -function isValue(binding: any) { - return isObservable(binding) && unwrap(binding).hasOwnProperty('value') -} export const RenderProxyHandlers = { get(target: ComponentInstance, key: string) { const { state, props } = target if (state.hasOwnProperty(key)) { - const value = state[key] - return isValue(value) ? value.value : value + return state[key] } else if (props.hasOwnProperty(key)) { return props[key] } else { @@ -34,12 +27,7 @@ export const RenderProxyHandlers = { set(target: ComponentInstance, key: string, value: any): boolean { const { state } = target if (state.hasOwnProperty(key)) { - const binding = state[key] - if (isValue(binding)) { - binding.value = value - } else { - state[key] = value - } + state[key] = value return true } else { if (__DEV__) { diff --git a/packages/runtime-core/src/createRenderer.ts b/packages/runtime-core/src/createRenderer.ts index 3edf4866..ba8ff88a 100644 --- a/packages/runtime-core/src/createRenderer.ts +++ b/packages/runtime-core/src/createRenderer.ts @@ -128,6 +128,7 @@ export function createRenderer(options: RendererOptions) { } else { if (__DEV__ && !isFunction(type) && !isObject(type)) { // TODO warn invalid node type + debugger } processComponent(n1, n2, container, anchor) }