wip: value()

This commit is contained in:
Evan You 2019-05-29 17:36:53 +08:00
parent 453cdcd600
commit 02421dbe62
6 changed files with 49 additions and 18 deletions

View File

@ -3,6 +3,7 @@ import { OperationTypes } from './operations'
import { track, trigger } from './effect' import { track, trigger } from './effect'
import { LOCKED } from './lock' import { LOCKED } from './lock'
import { isObject } from '@vue/shared' import { isObject } from '@vue/shared'
import { isValue } from './value'
const hasOwnProperty = Object.prototype.hasOwnProperty const hasOwnProperty = Object.prototype.hasOwnProperty
@ -18,6 +19,9 @@ function createGetter(isImmutable: boolean) {
if (typeof key === 'symbol' && builtInSymbols.has(key)) { if (typeof key === 'symbol' && builtInSymbols.has(key)) {
return res return res
} }
if (isValue(res)) {
return res.value
}
track(target, OperationTypes.GET, key) track(target, OperationTypes.GET, key)
return isObject(res) return isObject(res)
? isImmutable ? isImmutable
@ -38,6 +42,10 @@ function set(
value = unwrap(value) value = unwrap(value)
const hadKey = hasOwnProperty.call(target, key) const hadKey = hasOwnProperty.call(target, key)
const oldValue = target[key] const oldValue = target[key]
if (isValue(oldValue)) {
oldValue.value = value
return true
}
const result = Reflect.set(target, key, value, receiver) const result = Reflect.set(target, key, value, receiver)
// don't trigger if target is something up in the prototype chain of original // don't trigger if target is something up in the prototype chain of original
if (target === unwrap(receiver)) { if (target === unwrap(receiver)) {

View File

@ -1,5 +1,5 @@
import { OperationTypes } from './operations' import { OperationTypes } from './operations'
import { Dep, KeyToDepMap, targetMap } from './state' import { Dep, targetMap } from './state'
export interface ReactiveEffect { export interface ReactiveEffect {
(): any (): any
@ -84,8 +84,10 @@ export function track(
if (type === OperationTypes.ITERATE) { if (type === OperationTypes.ITERATE) {
key = ITERATE_KEY key = ITERATE_KEY
} }
// keyMap must exist because only an observed target can call this function let depsMap = targetMap.get(target)
const depsMap = targetMap.get(target) as KeyToDepMap if (depsMap === void 0) {
targetMap.set(target, (depsMap = new Map()))
}
let dep = depsMap.get(key as string | symbol) let dep = depsMap.get(key as string | symbol)
if (!dep) { if (!dep) {
depsMap.set(key as string | symbol, (dep = new Set())) depsMap.set(key as string | symbol, (dep = new Set()))
@ -111,7 +113,11 @@ export function trigger(
key?: string | symbol, key?: string | symbol,
extraInfo?: any 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 effects = new Set()
const computedRunners = new Set() const computedRunners = new Set()
if (type === OperationTypes.CLEAR) { if (type === OperationTypes.CLEAR) {

View File

@ -28,6 +28,7 @@ export { ReactiveEffect, ReactiveEffectOptions, DebuggerEvent }
export { OperationTypes } from './operations' export { OperationTypes } from './operations'
export { computed, ComputedGetter } from './computed' export { computed, ComputedGetter } from './computed'
export { lock, unlock } from './lock' export { lock, unlock } from './lock'
export { value, isValue } from './value'
const collectionTypes: Set<any> = new Set([Set, Map, WeakMap, WeakSet]) const collectionTypes: Set<any> = new Set([Set, Map, WeakMap, WeakSet])
const observableValueRE = /^\[object (?:Object|Array|Map|Set|WeakMap|WeakSet)\]$/ const observableValueRE = /^\[object (?:Object|Array|Map|Set|WeakMap|WeakSet)\]$/

View File

@ -0,0 +1,27 @@
import { track, trigger } from './effect'
import { OperationTypes } from './operations'
const knownValues = new WeakSet()
export interface Value<T> {
value: T
}
export function value<T>(raw: T): Value<T> {
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)
}

View File

@ -1,17 +1,10 @@
import { ComponentInstance } from './component' 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 = { export const RenderProxyHandlers = {
get(target: ComponentInstance, key: string) { get(target: ComponentInstance, key: string) {
const { state, props } = target const { state, props } = target
if (state.hasOwnProperty(key)) { if (state.hasOwnProperty(key)) {
const value = state[key] return state[key]
return isValue(value) ? value.value : value
} else if (props.hasOwnProperty(key)) { } else if (props.hasOwnProperty(key)) {
return props[key] return props[key]
} else { } else {
@ -34,12 +27,7 @@ export const RenderProxyHandlers = {
set(target: ComponentInstance, key: string, value: any): boolean { set(target: ComponentInstance, key: string, value: any): boolean {
const { state } = target const { state } = target
if (state.hasOwnProperty(key)) { if (state.hasOwnProperty(key)) {
const binding = state[key] state[key] = value
if (isValue(binding)) {
binding.value = value
} else {
state[key] = value
}
return true return true
} else { } else {
if (__DEV__) { if (__DEV__) {

View File

@ -128,6 +128,7 @@ export function createRenderer(options: RendererOptions) {
} else { } else {
if (__DEV__ && !isFunction(type) && !isObject(type)) { if (__DEV__ && !isFunction(type) && !isObject(type)) {
// TODO warn invalid node type // TODO warn invalid node type
debugger
} }
processComponent(n1, n2, container, anchor) processComponent(n1, n2, container, anchor)
} }