From bfe69873237e67d23721624c550d29f5f2efbf13 Mon Sep 17 00:00:00 2001 From: Evan You Date: Wed, 12 Jun 2019 00:03:50 +0800 Subject: [PATCH] refactor: adjust reactivity structure --- packages/reactivity/__tests__/value.spec.ts | 3 +- packages/reactivity/src/effect.ts | 29 +++- packages/reactivity/src/index.ts | 172 ++------------------ packages/reactivity/src/state.ts | 128 ++++++++++++++- 4 files changed, 164 insertions(+), 168 deletions(-) diff --git a/packages/reactivity/__tests__/value.spec.ts b/packages/reactivity/__tests__/value.spec.ts index f72026fd76..3ad687fd16 100644 --- a/packages/reactivity/__tests__/value.spec.ts +++ b/packages/reactivity/__tests__/value.spec.ts @@ -1,5 +1,4 @@ -import { value } from '../src/value' -import { effect, state } from '../src/index' +import { value, effect, state } from '../src/index' describe('observer/value', () => { it('should hold a value', () => { diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index 9d9295c022..51adc4a5ce 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -1,5 +1,6 @@ import { OperationTypes } from './operations' import { Dep, targetMap } from './state' +import { EMPTY_OBJ } from '@vue/shared' export interface ReactiveEffect { (): any @@ -38,7 +39,31 @@ export const activeReactiveEffectStack: ReactiveEffect[] = [] export const ITERATE_KEY = Symbol('iterate') -export function createReactiveEffect( +export function effect( + fn: Function, + options: ReactiveEffectOptions = EMPTY_OBJ +): ReactiveEffect { + if ((fn as ReactiveEffect).isEffect) { + fn = (fn as ReactiveEffect).raw + } + const effect = createReactiveEffect(fn, options) + if (!options.lazy) { + effect() + } + return effect +} + +export function stop(effect: ReactiveEffect) { + if (effect.active) { + cleanup(effect) + if (effect.onStop) { + effect.onStop() + } + effect.active = false + } +} + +function createReactiveEffect( fn: Function, options: ReactiveEffectOptions ): ReactiveEffect { @@ -72,7 +97,7 @@ function run(effect: ReactiveEffect, fn: Function, args: any[]): any { } } -export function cleanup(effect: ReactiveEffect) { +function cleanup(effect: ReactiveEffect) { const { deps } = effect if (deps.length) { for (let i = 0; i < deps.length; i++) { diff --git a/packages/reactivity/src/index.ts b/packages/reactivity/src/index.ts index 28f895198d..3bc0572012 100644 --- a/packages/reactivity/src/index.ts +++ b/packages/reactivity/src/index.ts @@ -1,164 +1,20 @@ -import { isObject, EMPTY_OBJ } from '@vue/shared' -import { mutableHandlers, immutableHandlers } from './baseHandlers' - -import { - mutableCollectionHandlers, - immutableCollectionHandlers -} from './collectionHandlers' - -import { - targetMap, - observedToRaw, - rawToObserved, - immutableToRaw, - rawToImmutable, - immutableValues, - nonReactiveValues +export { value, isValue, Value, UnwrapValue } from './value' +export { + state, + isState, + immutableState, + isImmutableState, + toRaw, + markImmutable, + markNonReactive } from './state' - -import { - createReactiveEffect, - cleanup, +export { computed, ComputedValue } from './computed' +export { + effect, + stop, ReactiveEffect, ReactiveEffectOptions, DebuggerEvent } from './effect' - -import { UnwrapValue } from './value' - -export { ReactiveEffect, ReactiveEffectOptions, DebuggerEvent } -export { OperationTypes } from './operations' -export { computed, ComputedValue } from './computed' export { lock, unlock } from './lock' -export { value, isValue, Value, UnwrapValue } from './value' - -const collectionTypes: Set = new Set([Set, Map, WeakMap, WeakSet]) -const observableValueRE = /^\[object (?:Object|Array|Map|Set|WeakMap|WeakSet)\]$/ - -const canObserve = (value: any): boolean => { - return ( - !value._isVue && - !value._isVNode && - observableValueRE.test(Object.prototype.toString.call(value)) && - !nonReactiveValues.has(value) - ) -} - -type ObservableFactory = (target?: T) => UnwrapValue - -export const state = ((target: any = {}): any => { - // if trying to observe an immutable proxy, return the immutable version. - if (immutableToRaw.has(target)) { - return target - } - // target is explicitly marked as immutable by user - if (immutableValues.has(target)) { - return immutableState(target) - } - return createObservable( - target, - rawToObserved, - observedToRaw, - mutableHandlers, - mutableCollectionHandlers - ) -}) as ObservableFactory - -export const immutableState = ((target: any = {}): any => { - // value is a mutable observable, retrive its original and return - // a readonly version. - if (observedToRaw.has(target)) { - target = observedToRaw.get(target) - } - return createObservable( - target, - rawToImmutable, - immutableToRaw, - immutableHandlers, - immutableCollectionHandlers - ) -}) as ObservableFactory - -function createObservable( - target: any, - toProxy: WeakMap, - toRaw: WeakMap, - baseHandlers: ProxyHandler, - collectionHandlers: ProxyHandler -) { - if (!isObject(target)) { - if (__DEV__) { - console.warn(`value is not observable: ${String(target)}`) - } - return target - } - // target already has corresponding Proxy - let observed = toProxy.get(target) - if (observed !== void 0) { - return observed - } - // target is already a Proxy - if (toRaw.has(target)) { - return target - } - // only a whitelist of value types can be observed. - if (!canObserve(target)) { - return target - } - const handlers = collectionTypes.has(target.constructor) - ? collectionHandlers - : baseHandlers - observed = new Proxy(target, handlers) - toProxy.set(target, observed) - toRaw.set(observed, target) - if (!targetMap.has(target)) { - targetMap.set(target, new Map()) - } - return observed -} - -export function effect( - fn: Function, - options: ReactiveEffectOptions = EMPTY_OBJ -): ReactiveEffect { - if ((fn as ReactiveEffect).isEffect) { - fn = (fn as ReactiveEffect).raw - } - const effect = createReactiveEffect(fn, options) - if (!options.lazy) { - effect() - } - return effect -} - -export function stop(effect: ReactiveEffect) { - if (effect.active) { - cleanup(effect) - if (effect.onStop) { - effect.onStop() - } - effect.active = false - } -} - -export function isState(value: any): boolean { - return observedToRaw.has(value) || immutableToRaw.has(value) -} - -export function isImmutableState(value: any): boolean { - return immutableToRaw.has(value) -} - -export function toRaw(observed: T): T { - return observedToRaw.get(observed) || immutableToRaw.get(observed) || observed -} - -export function markImmutable(value: T): T { - immutableValues.add(value) - return value -} - -export function markNonReactive(value: T): T { - nonReactiveValues.add(value) - return value -} +export { OperationTypes } from './operations' diff --git a/packages/reactivity/src/state.ts b/packages/reactivity/src/state.ts index eee65d22f6..0f990ebbeb 100644 --- a/packages/reactivity/src/state.ts +++ b/packages/reactivity/src/state.ts @@ -1,3 +1,12 @@ +import { isObject } from '@vue/shared' +import { mutableHandlers, immutableHandlers } from './baseHandlers' + +import { + mutableCollectionHandlers, + immutableCollectionHandlers +} from './collectionHandlers' + +import { UnwrapValue } from './value' import { ReactiveEffect } from './effect' // The main WeakMap that stores {target -> key -> dep} connections. @@ -9,12 +18,119 @@ export type KeyToDepMap = Map export const targetMap: WeakMap = new WeakMap() // WeakMaps that store {raw <-> observed} pairs. -export const rawToObserved: WeakMap = new WeakMap() -export const observedToRaw: WeakMap = new WeakMap() -export const rawToImmutable: WeakMap = new WeakMap() -export const immutableToRaw: WeakMap = new WeakMap() +const rawToObserved: WeakMap = new WeakMap() +const observedToRaw: WeakMap = new WeakMap() +const rawToImmutable: WeakMap = new WeakMap() +const immutableToRaw: WeakMap = new WeakMap() // WeakSets for values that are marked immutable or non-reactive during // observable creation. -export const immutableValues: WeakSet = new WeakSet() -export const nonReactiveValues: WeakSet = new WeakSet() +const immutableValues: WeakSet = new WeakSet() +const nonReactiveValues: WeakSet = new WeakSet() + +const collectionTypes: Set = new Set([Set, Map, WeakMap, WeakSet]) +const observableValueRE = /^\[object (?:Object|Array|Map|Set|WeakMap|WeakSet)\]$/ + +const canObserve = (value: any): boolean => { + return ( + !value._isVue && + !value._isVNode && + observableValueRE.test(Object.prototype.toString.call(value)) && + !nonReactiveValues.has(value) + ) +} + +type ObservableFactory = (target?: T) => UnwrapValue + +export const state = ((target: any = {}): any => { + // if trying to observe an immutable proxy, return the immutable version. + if (immutableToRaw.has(target)) { + return target + } + // target is explicitly marked as immutable by user + if (immutableValues.has(target)) { + return immutableState(target) + } + return createObservable( + target, + rawToObserved, + observedToRaw, + mutableHandlers, + mutableCollectionHandlers + ) +}) as ObservableFactory + +export const immutableState = ((target: any = {}): any => { + // value is a mutable observable, retrive its original and return + // a readonly version. + if (observedToRaw.has(target)) { + target = observedToRaw.get(target) + } + return createObservable( + target, + rawToImmutable, + immutableToRaw, + immutableHandlers, + immutableCollectionHandlers + ) +}) as ObservableFactory + +function createObservable( + target: any, + toProxy: WeakMap, + toRaw: WeakMap, + baseHandlers: ProxyHandler, + collectionHandlers: ProxyHandler +) { + if (!isObject(target)) { + if (__DEV__) { + console.warn(`value is not observable: ${String(target)}`) + } + return target + } + // target already has corresponding Proxy + let observed = toProxy.get(target) + if (observed !== void 0) { + return observed + } + // target is already a Proxy + if (toRaw.has(target)) { + return target + } + // only a whitelist of value types can be observed. + if (!canObserve(target)) { + return target + } + const handlers = collectionTypes.has(target.constructor) + ? collectionHandlers + : baseHandlers + observed = new Proxy(target, handlers) + toProxy.set(target, observed) + toRaw.set(observed, target) + if (!targetMap.has(target)) { + targetMap.set(target, new Map()) + } + return observed +} + +export function isState(value: any): boolean { + return observedToRaw.has(value) || immutableToRaw.has(value) +} + +export function isImmutableState(value: any): boolean { + return immutableToRaw.has(value) +} + +export function toRaw(observed: T): T { + return observedToRaw.get(observed) || immutableToRaw.get(observed) || observed +} + +export function markImmutable(value: T): T { + immutableValues.add(value) + return value +} + +export function markNonReactive(value: T): T { + nonReactiveValues.add(value) + return value +} -- 2.47.3