wip: state -> reactive, value -> ref

This commit is contained in:
Evan You
2019-08-16 09:42:46 -04:00
parent 09141b56fd
commit caba6d5c9e
17 changed files with 398 additions and 400 deletions

View File

@@ -1,9 +1,9 @@
import { state, immutableState, toRaw } from './index'
import { reactive, immutable, toRaw } from './reactive'
import { OperationTypes } from './operations'
import { track, trigger } from './effect'
import { LOCKED } from './lock'
import { isObject } from '@vue/shared'
import { isValue } from './value'
import { isRef } from './ref'
const hasOwnProperty = Object.prototype.hasOwnProperty
@@ -19,16 +19,16 @@ function createGetter(isImmutable: boolean) {
if (typeof key === 'symbol' && builtInSymbols.has(key)) {
return res
}
if (isValue(res)) {
if (isRef(res)) {
return res.value
}
track(target, OperationTypes.GET, key)
return isObject(res)
? isImmutable
? // need to lazy access immutable and observable here to avoid
? // need to lazy access immutable and reactive here to avoid
// circular dependency
immutableState(res)
: state(res)
immutable(res)
: reactive(res)
: res
}
}
@@ -42,7 +42,7 @@ function set(
value = toRaw(value)
const hadKey = hasOwnProperty.call(target, key)
const oldValue = target[key]
if (isValue(oldValue) && !isValue(value)) {
if (isRef(oldValue) && !isRef(value)) {
oldValue.value = value
return true
}

View File

@@ -1,12 +1,11 @@
import { toRaw, state, immutableState } from './index'
import { toRaw, reactive, immutable } from './reactive'
import { track, trigger } from './effect'
import { OperationTypes } from './operations'
import { LOCKED } from './lock'
import { isObject } from '@vue/shared'
const toObservable = (value: any) => (isObject(value) ? state(value) : value)
const toImmutable = (value: any) =>
isObject(value) ? immutableState(value) : value
const toReactive = (value: any) => (isObject(value) ? reactive(value) : value)
const toImmutable = (value: any) => (isObject(value) ? immutable(value) : value)
function get(target: any, key: any, wrap: (t: any) => any): any {
target = toRaw(target)
@@ -117,7 +116,7 @@ function createForEach(isImmutable: boolean) {
const observed = this
const target = toRaw(observed)
const proto: any = Reflect.getPrototypeOf(target)
const wrap = isImmutable ? toImmutable : toObservable
const wrap = isImmutable ? toImmutable : toReactive
track(target, OperationTypes.ITERATE)
// important: create sure the callback is
// 1. invoked with the observable map as `this` and 3rd arg
@@ -137,7 +136,7 @@ function createIterableMethod(method: string | symbol, isImmutable: boolean) {
method === 'entries' ||
(method === Symbol.iterator && target instanceof Map)
const innerIterator = proto[method].apply(target, args)
const wrap = isImmutable ? toImmutable : toObservable
const wrap = isImmutable ? toImmutable : toReactive
track(target, OperationTypes.ITERATE)
// return a wrapped iterator which returns observed versions of the
// values emitted from the real iterator
@@ -182,7 +181,7 @@ function createImmutableMethod(
const mutableInstrumentations: any = {
get(key: any) {
return get(this, key, toObservable)
return get(this, key, toReactive)
},
get size() {
return size(this)

View File

@@ -1,8 +1,7 @@
import { effect } from './index'
import { ReactiveEffect, activeReactiveEffectStack } from './effect'
import { knownValues } from './value'
import { effect, ReactiveEffect, activeReactiveEffectStack } from './effect'
import { knownValues } from './ref'
export interface ComputedValue<T> {
export interface ComputedRef<T> {
readonly value: T
readonly effect: ReactiveEffect
}
@@ -10,7 +9,7 @@ export interface ComputedValue<T> {
export function computed<T>(
getter: () => T,
setter?: (v: T) => void
): ComputedValue<T> {
): ComputedRef<T> {
let dirty: boolean = true
let value: any = undefined
const runner = effect(getter, {

View File

@@ -1,5 +1,5 @@
import { OperationTypes } from './operations'
import { Dep, targetMap } from './state'
import { Dep, targetMap } from './reactive'
import { EMPTY_OBJ } from '@vue/shared'
export interface ReactiveEffect {

View File

@@ -1,14 +1,14 @@
export { value, isValue, Value, UnwrapValue } from './value'
export { ref, isRef, Ref, UnwrapRef } from './ref'
export {
state,
isState,
immutableState,
isImmutableState,
reactive,
isReactive,
immutable,
isImmutable,
toRaw,
markImmutable,
markNonReactive
} from './state'
export { computed, ComputedValue } from './computed'
} from './reactive'
export { computed, ComputedRef } from './computed'
export {
effect,
stop,

View File

@@ -6,7 +6,7 @@ import {
immutableCollectionHandlers
} from './collectionHandlers'
import { UnwrapValue } from './value'
import { UnwrapRef } from './ref'
import { ReactiveEffect } from './effect'
// The main WeakMap that stores {target -> key -> dep} connections.
@@ -40,16 +40,16 @@ const canObserve = (value: any): boolean => {
)
}
type ObservableFactory = <T>(target?: T) => UnwrapValue<T>
type ObservableFactory = <T>(target?: T) => UnwrapRef<T>
export const state = ((target: any = {}): any => {
export const reactive = ((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 immutable(target)
}
return createObservable(
target,
@@ -60,7 +60,7 @@ export const state = ((target: any = {}): any => {
)
}) as ObservableFactory
export const immutableState = ((target: any = {}): any => {
export const immutable = ((target: any = {}): any => {
// value is a mutable observable, retrive its original and return
// a readonly version.
if (observedToRaw.has(target)) {
@@ -113,11 +113,11 @@ function createObservable(
return observed
}
export function isState(value: any): boolean {
export function isReactive(value: any): boolean {
return observedToRaw.has(value) || immutableToRaw.has(value)
}
export function isImmutableState(value: any): boolean {
export function isImmutable(value: any): boolean {
return immutableToRaw.has(value)
}

View File

@@ -0,0 +1,118 @@
import { track, trigger } from './effect'
import { OperationTypes } from './operations'
import { isObject } from '@vue/shared'
import { reactive } from './reactive'
export const knownValues = new WeakSet()
export interface Ref<T> {
value: T
}
const convert = (val: any): any => (isObject(val) ? reactive(val) : val)
export function ref<T>(raw: T): Ref<T> {
raw = convert(raw)
const v = {
get value() {
track(v, OperationTypes.GET, '')
return raw
},
set value(newVal) {
raw = convert(newVal)
trigger(v, OperationTypes.SET, '')
}
}
knownValues.add(v)
return v
}
export function isRef(v: any): v is Ref<any> {
return knownValues.has(v)
}
type BailTypes =
| Function
| Map<any, any>
| Set<any>
| WeakMap<any, any>
| WeakSet<any>
// Recursively unwraps nested value bindings.
// Unfortunately TS cannot do recursive types, but this should be enough for
// practical use cases...
export type UnwrapRef<T> = T extends Ref<infer V>
? UnwrapRef2<V>
: T extends Array<infer V>
? Array<UnwrapRef2<V>>
: T extends BailTypes
? T // bail out on types that shouldn't be unwrapped
: T extends object ? { [K in keyof T]: UnwrapRef2<T[K]> } : T
type UnwrapRef2<T> = T extends Ref<infer V>
? UnwrapRef3<V>
: T extends Array<infer V>
? Array<UnwrapRef3<V>>
: T extends BailTypes
? T
: T extends object ? { [K in keyof T]: UnwrapRef3<T[K]> } : T
type UnwrapRef3<T> = T extends Ref<infer V>
? UnwrapRef4<V>
: T extends Array<infer V>
? Array<UnwrapRef4<V>>
: T extends BailTypes
? T
: T extends object ? { [K in keyof T]: UnwrapRef4<T[K]> } : T
type UnwrapRef4<T> = T extends Ref<infer V>
? UnwrapRef5<V>
: T extends Array<infer V>
? Array<UnwrapRef5<V>>
: T extends BailTypes
? T
: T extends object ? { [K in keyof T]: UnwrapRef5<T[K]> } : T
type UnwrapRef5<T> = T extends Ref<infer V>
? UnwrapRef6<V>
: T extends Array<infer V>
? Array<UnwrapRef6<V>>
: T extends BailTypes
? T
: T extends object ? { [K in keyof T]: UnwrapRef6<T[K]> } : T
type UnwrapRef6<T> = T extends Ref<infer V>
? UnwrapRef7<V>
: T extends Array<infer V>
? Array<UnwrapRef7<V>>
: T extends BailTypes
? T
: T extends object ? { [K in keyof T]: UnwrapRef7<T[K]> } : T
type UnwrapRef7<T> = T extends Ref<infer V>
? UnwrapRef8<V>
: T extends Array<infer V>
? Array<UnwrapRef8<V>>
: T extends BailTypes
? T
: T extends object ? { [K in keyof T]: UnwrapRef8<T[K]> } : T
type UnwrapRef8<T> = T extends Ref<infer V>
? UnwrapRef9<V>
: T extends Array<infer V>
? Array<UnwrapRef9<V>>
: T extends BailTypes
? T
: T extends object ? { [K in keyof T]: UnwrapRef9<T[K]> } : T
type UnwrapRef9<T> = T extends Ref<infer V>
? UnwrapRef10<V>
: T extends Array<infer V>
? Array<UnwrapRef10<V>>
: T extends BailTypes
? T
: T extends object ? { [K in keyof T]: UnwrapRef10<T[K]> } : T
type UnwrapRef10<T> = T extends Ref<infer V>
? V // stop recursion
: T

View File

@@ -1,118 +0,0 @@
import { track, trigger } from './effect'
import { OperationTypes } from './operations'
import { isObject } from '@vue/shared'
import { state } from './index'
export const knownValues = new WeakSet()
export interface Value<T> {
value: T
}
const convert = (val: any): any => (isObject(val) ? state(val) : val)
export function value<T>(raw: T): Value<T> {
raw = convert(raw)
const v = {
get value() {
track(v, OperationTypes.GET, '')
return raw
},
set value(newVal) {
raw = convert(newVal)
trigger(v, OperationTypes.SET, '')
}
}
knownValues.add(v)
return v
}
export function isValue(v: any): v is Value<any> {
return knownValues.has(v)
}
type BailTypes =
| Function
| Map<any, any>
| Set<any>
| WeakMap<any, any>
| WeakSet<any>
// Recursively unwraps nested value bindings.
// Unfortunately TS cannot do recursive types, but this should be enough for
// practical use cases...
export type UnwrapValue<T> = T extends Value<infer V>
? UnwrapValue2<V>
: T extends Array<infer V>
? Array<UnwrapValue2<V>>
: T extends BailTypes
? T // bail out on types that shouldn't be unwrapped
: T extends object ? { [K in keyof T]: UnwrapValue2<T[K]> } : T
type UnwrapValue2<T> = T extends Value<infer V>
? UnwrapValue3<V>
: T extends Array<infer V>
? Array<UnwrapValue3<V>>
: T extends BailTypes
? T
: T extends object ? { [K in keyof T]: UnwrapValue3<T[K]> } : T
type UnwrapValue3<T> = T extends Value<infer V>
? UnwrapValue4<V>
: T extends Array<infer V>
? Array<UnwrapValue4<V>>
: T extends BailTypes
? T
: T extends object ? { [K in keyof T]: UnwrapValue4<T[K]> } : T
type UnwrapValue4<T> = T extends Value<infer V>
? UnwrapValue5<V>
: T extends Array<infer V>
? Array<UnwrapValue5<V>>
: T extends BailTypes
? T
: T extends object ? { [K in keyof T]: UnwrapValue5<T[K]> } : T
type UnwrapValue5<T> = T extends Value<infer V>
? UnwrapValue6<V>
: T extends Array<infer V>
? Array<UnwrapValue6<V>>
: T extends BailTypes
? T
: T extends object ? { [K in keyof T]: UnwrapValue6<T[K]> } : T
type UnwrapValue6<T> = T extends Value<infer V>
? UnwrapValue7<V>
: T extends Array<infer V>
? Array<UnwrapValue7<V>>
: T extends BailTypes
? T
: T extends object ? { [K in keyof T]: UnwrapValue7<T[K]> } : T
type UnwrapValue7<T> = T extends Value<infer V>
? UnwrapValue8<V>
: T extends Array<infer V>
? Array<UnwrapValue8<V>>
: T extends BailTypes
? T
: T extends object ? { [K in keyof T]: UnwrapValue8<T[K]> } : T
type UnwrapValue8<T> = T extends Value<infer V>
? UnwrapValue9<V>
: T extends Array<infer V>
? Array<UnwrapValue9<V>>
: T extends BailTypes
? T
: T extends object ? { [K in keyof T]: UnwrapValue9<T[K]> } : T
type UnwrapValue9<T> = T extends Value<infer V>
? UnwrapValue10<V>
: T extends Array<infer V>
? Array<UnwrapValue10<V>>
: T extends BailTypes
? T
: T extends object ? { [K in keyof T]: UnwrapValue10<T[K]> } : T
type UnwrapValue10<T> = T extends Value<infer V>
? V // stop recursion
: T