Merge remote-tracking branch 'github/master' into changing_unwrap_ref
This commit is contained in:
@@ -5,9 +5,6 @@ import {
|
||||
isReactive,
|
||||
isReadonly,
|
||||
markNonReactive,
|
||||
markReadonly,
|
||||
lock,
|
||||
unlock,
|
||||
effect,
|
||||
ref,
|
||||
shallowReadonly
|
||||
@@ -91,22 +88,7 @@ describe('reactivity/readonly', () => {
|
||||
).toHaveBeenWarnedLast()
|
||||
})
|
||||
|
||||
it('should allow mutation when unlocked', () => {
|
||||
const observed: any = readonly({ foo: 1, bar: { baz: 2 } })
|
||||
unlock()
|
||||
observed.prop = 2
|
||||
observed.bar.qux = 3
|
||||
delete observed.bar.baz
|
||||
delete observed.foo
|
||||
lock()
|
||||
expect(observed.prop).toBe(2)
|
||||
expect(observed.foo).toBeUndefined()
|
||||
expect(observed.bar.qux).toBe(3)
|
||||
expect('baz' in observed.bar).toBe(false)
|
||||
expect(`target is readonly`).not.toHaveBeenWarned()
|
||||
})
|
||||
|
||||
it('should not trigger effects when locked', () => {
|
||||
it('should not trigger effects', () => {
|
||||
const observed: any = readonly({ a: 1 })
|
||||
let dummy
|
||||
effect(() => {
|
||||
@@ -118,20 +100,6 @@ describe('reactivity/readonly', () => {
|
||||
expect(dummy).toBe(1)
|
||||
expect(`target is readonly`).toHaveBeenWarned()
|
||||
})
|
||||
|
||||
it('should trigger effects when unlocked', () => {
|
||||
const observed: any = readonly({ a: 1 })
|
||||
let dummy
|
||||
effect(() => {
|
||||
dummy = observed.a
|
||||
})
|
||||
expect(dummy).toBe(1)
|
||||
unlock()
|
||||
observed.a = 2
|
||||
lock()
|
||||
expect(observed.a).toBe(2)
|
||||
expect(dummy).toBe(2)
|
||||
})
|
||||
})
|
||||
|
||||
describe('Array', () => {
|
||||
@@ -183,23 +151,7 @@ describe('reactivity/readonly', () => {
|
||||
expect(`target is readonly.`).toHaveBeenWarnedTimes(5)
|
||||
})
|
||||
|
||||
it('should allow mutation when unlocked', () => {
|
||||
const observed: any = readonly([{ foo: 1, bar: { baz: 2 } }])
|
||||
unlock()
|
||||
observed[1] = 2
|
||||
observed.push(3)
|
||||
observed[0].foo = 2
|
||||
observed[0].bar.baz = 3
|
||||
lock()
|
||||
expect(observed.length).toBe(3)
|
||||
expect(observed[1]).toBe(2)
|
||||
expect(observed[2]).toBe(3)
|
||||
expect(observed[0].foo).toBe(2)
|
||||
expect(observed[0].bar.baz).toBe(3)
|
||||
expect(`target is readonly`).not.toHaveBeenWarned()
|
||||
})
|
||||
|
||||
it('should not trigger effects when locked', () => {
|
||||
it('should not trigger effects', () => {
|
||||
const observed: any = readonly([{ a: 1 }])
|
||||
let dummy
|
||||
effect(() => {
|
||||
@@ -215,30 +167,6 @@ describe('reactivity/readonly', () => {
|
||||
expect(dummy).toBe(1)
|
||||
expect(`target is readonly`).toHaveBeenWarnedTimes(2)
|
||||
})
|
||||
|
||||
it('should trigger effects when unlocked', () => {
|
||||
const observed: any = readonly([{ a: 1 }])
|
||||
let dummy
|
||||
effect(() => {
|
||||
dummy = observed[0].a
|
||||
})
|
||||
expect(dummy).toBe(1)
|
||||
|
||||
unlock()
|
||||
|
||||
observed[0].a = 2
|
||||
expect(observed[0].a).toBe(2)
|
||||
expect(dummy).toBe(2)
|
||||
|
||||
observed[0] = { a: 3 }
|
||||
expect(observed[0].a).toBe(3)
|
||||
expect(dummy).toBe(3)
|
||||
|
||||
observed.unshift({ a: 4 })
|
||||
expect(observed[0].a).toBe(4)
|
||||
expect(dummy).toBe(4)
|
||||
lock()
|
||||
})
|
||||
})
|
||||
|
||||
const maps = [Map, WeakMap]
|
||||
@@ -276,23 +204,6 @@ describe('reactivity/readonly', () => {
|
||||
).toHaveBeenWarned()
|
||||
})
|
||||
|
||||
test('should allow mutation & trigger effect when unlocked', () => {
|
||||
const map = readonly(new Collection())
|
||||
const isWeak = Collection === WeakMap
|
||||
const key = {}
|
||||
let dummy
|
||||
effect(() => {
|
||||
dummy = map.get(key) + (isWeak ? 0 : map.size)
|
||||
})
|
||||
expect(dummy).toBeNaN()
|
||||
unlock()
|
||||
map.set(key, 1)
|
||||
lock()
|
||||
expect(dummy).toBe(isWeak ? 1 : 2)
|
||||
expect(map.get(key)).toBe(1)
|
||||
expect(`target is readonly`).not.toHaveBeenWarned()
|
||||
})
|
||||
|
||||
if (Collection === Map) {
|
||||
test('should retrieve readonly values on iteration', () => {
|
||||
const key1 = {}
|
||||
@@ -347,22 +258,6 @@ describe('reactivity/readonly', () => {
|
||||
).toHaveBeenWarned()
|
||||
})
|
||||
|
||||
test('should allow mutation & trigger effect when unlocked', () => {
|
||||
const set = readonly(new Collection())
|
||||
const key = {}
|
||||
let dummy
|
||||
effect(() => {
|
||||
dummy = set.has(key)
|
||||
})
|
||||
expect(dummy).toBe(false)
|
||||
unlock()
|
||||
set.add(key)
|
||||
lock()
|
||||
expect(dummy).toBe(true)
|
||||
expect(set.has(key)).toBe(true)
|
||||
expect(`target is readonly`).not.toHaveBeenWarned()
|
||||
})
|
||||
|
||||
if (Collection === Set) {
|
||||
test('should retrieve readonly values on iteration', () => {
|
||||
const original = new Collection([{}, {}])
|
||||
@@ -401,6 +296,19 @@ describe('reactivity/readonly', () => {
|
||||
expect(toRaw(a)).toBe(toRaw(b))
|
||||
})
|
||||
|
||||
test('readonly should track and trigger if wrapping reactive original', () => {
|
||||
const a = reactive({ n: 1 })
|
||||
const b = readonly(a)
|
||||
let dummy
|
||||
effect(() => {
|
||||
dummy = b.n
|
||||
})
|
||||
expect(dummy).toBe(1)
|
||||
a.n++
|
||||
expect(b.n).toBe(2)
|
||||
expect(dummy).toBe(2)
|
||||
})
|
||||
|
||||
test('observing already observed value should return same Proxy', () => {
|
||||
const original = { foo: 1 }
|
||||
const observed = readonly(original)
|
||||
@@ -424,17 +332,6 @@ describe('reactivity/readonly', () => {
|
||||
expect(isReactive(obj.bar)).toBe(false)
|
||||
})
|
||||
|
||||
test('markReadonly', () => {
|
||||
const obj = reactive({
|
||||
foo: { a: 1 },
|
||||
bar: markReadonly({ b: 2 })
|
||||
})
|
||||
expect(isReactive(obj.foo)).toBe(true)
|
||||
expect(isReactive(obj.bar)).toBe(true)
|
||||
expect(isReadonly(obj.foo)).toBe(false)
|
||||
expect(isReadonly(obj.bar)).toBe(true)
|
||||
})
|
||||
|
||||
test('should make ref readonly', () => {
|
||||
const n: any = readonly(ref(1))
|
||||
n.value = 2
|
||||
@@ -470,13 +367,5 @@ describe('reactivity/readonly', () => {
|
||||
`Set operation on key "foo" failed: target is readonly.`
|
||||
).not.toHaveBeenWarned()
|
||||
})
|
||||
|
||||
test('should keep reactive properties reactive', () => {
|
||||
const props: any = shallowReadonly({ n: reactive({ foo: 1 }) })
|
||||
unlock()
|
||||
props.n = reactive({ foo: 2 })
|
||||
lock()
|
||||
expect(isReactive(props.n)).toBe(true)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -3,12 +3,13 @@ import {
|
||||
effect,
|
||||
reactive,
|
||||
isRef,
|
||||
toRef,
|
||||
toRefs,
|
||||
Ref,
|
||||
isReactive
|
||||
} from '../src/index'
|
||||
import { computed } from '@vue/runtime-dom'
|
||||
import { shallowRef, unref } from '../src/ref'
|
||||
import { shallowRef, unref, customRef } from '../src/ref'
|
||||
|
||||
describe('reactivity/ref', () => {
|
||||
it('should hold a value', () => {
|
||||
@@ -168,6 +169,34 @@ describe('reactivity/ref', () => {
|
||||
expect(isRef({ value: 0 })).toBe(false)
|
||||
})
|
||||
|
||||
test('toRef', () => {
|
||||
const a = reactive({
|
||||
x: 1
|
||||
})
|
||||
const x = toRef(a, 'x')
|
||||
expect(isRef(x)).toBe(true)
|
||||
expect(x.value).toBe(1)
|
||||
|
||||
// source -> proxy
|
||||
a.x = 2
|
||||
expect(x.value).toBe(2)
|
||||
|
||||
// proxy -> source
|
||||
x.value = 3
|
||||
expect(a.x).toBe(3)
|
||||
|
||||
// reactivity
|
||||
let dummyX
|
||||
effect(() => {
|
||||
dummyX = x.value
|
||||
})
|
||||
expect(dummyX).toBe(x.value)
|
||||
|
||||
// mutating source should trigger effect using the proxy refs
|
||||
a.x = 4
|
||||
expect(dummyX).toBe(4)
|
||||
})
|
||||
|
||||
test('toRefs', () => {
|
||||
const a = reactive({
|
||||
x: 1,
|
||||
@@ -208,4 +237,35 @@ describe('reactivity/ref', () => {
|
||||
expect(dummyX).toBe(4)
|
||||
expect(dummyY).toBe(5)
|
||||
})
|
||||
|
||||
test('customRef', () => {
|
||||
let value = 1
|
||||
let _trigger: () => void
|
||||
|
||||
const custom = customRef((track, trigger) => ({
|
||||
get() {
|
||||
track()
|
||||
return value
|
||||
},
|
||||
set(newValue: number) {
|
||||
value = newValue
|
||||
_trigger = trigger
|
||||
}
|
||||
}))
|
||||
|
||||
expect(isRef(custom)).toBe(true)
|
||||
|
||||
let dummy
|
||||
effect(() => {
|
||||
dummy = custom.value
|
||||
})
|
||||
expect(dummy).toBe(1)
|
||||
|
||||
custom.value = 2
|
||||
// should not trigger yet
|
||||
expect(dummy).toBe(1)
|
||||
|
||||
_trigger!()
|
||||
expect(dummy).toBe(2)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { reactive, readonly, toRaw } from './reactive'
|
||||
import { TrackOpTypes, TriggerOpTypes } from './operations'
|
||||
import { track, trigger, ITERATE_KEY } from './effect'
|
||||
import { LOCKED } from './lock'
|
||||
import { isObject, hasOwn, isSymbol, hasChanged, isArray } from '@vue/shared'
|
||||
import { isRef } from './ref'
|
||||
|
||||
@@ -12,7 +11,7 @@ const builtInSymbols = new Set(
|
||||
)
|
||||
|
||||
const get = /*#__PURE__*/ createGetter()
|
||||
const shallowReactiveGet = /*#__PURE__*/ createGetter(false, true)
|
||||
const shallowGet = /*#__PURE__*/ createGetter(false, true)
|
||||
const readonlyGet = /*#__PURE__*/ createGetter(true)
|
||||
const shallowReadonlyGet = /*#__PURE__*/ createGetter(true, true)
|
||||
|
||||
@@ -36,23 +35,32 @@ const arrayInstrumentations: Record<string, Function> = {}
|
||||
|
||||
function createGetter(isReadonly = false, shallow = false) {
|
||||
return function get(target: object, key: string | symbol, receiver: object) {
|
||||
if (isArray(target) && hasOwn(arrayInstrumentations, key)) {
|
||||
const targetIsArray = isArray(target)
|
||||
if (targetIsArray && hasOwn(arrayInstrumentations, key)) {
|
||||
return Reflect.get(arrayInstrumentations, key, receiver)
|
||||
}
|
||||
const res = Reflect.get(target, key, receiver)
|
||||
|
||||
if (isSymbol(key) && builtInSymbols.has(key)) {
|
||||
return res
|
||||
}
|
||||
|
||||
if (shallow) {
|
||||
track(target, TrackOpTypes.GET, key)
|
||||
// TODO strict mode that returns a shallow-readonly version of the value
|
||||
!isReadonly && track(target, TrackOpTypes.GET, key)
|
||||
return res
|
||||
}
|
||||
// ref unwrapping, only for Objects, not for Arrays.
|
||||
if (isRef(res) && !isArray(target)) {
|
||||
return res.value
|
||||
|
||||
if (isRef(res)) {
|
||||
if (targetIsArray) {
|
||||
!isReadonly && track(target, TrackOpTypes.GET, key)
|
||||
return res
|
||||
} else {
|
||||
// ref unwrapping, only for Objects, not for Arrays.
|
||||
return res.value
|
||||
}
|
||||
}
|
||||
track(target, TrackOpTypes.GET, key)
|
||||
|
||||
!isReadonly && track(target, TrackOpTypes.GET, key)
|
||||
return isObject(res)
|
||||
? isReadonly
|
||||
? // need to lazy access readonly and reactive here to avoid
|
||||
@@ -64,27 +72,15 @@ function createGetter(isReadonly = false, shallow = false) {
|
||||
}
|
||||
|
||||
const set = /*#__PURE__*/ createSetter()
|
||||
const shallowReactiveSet = /*#__PURE__*/ createSetter(false, true)
|
||||
const readonlySet = /*#__PURE__*/ createSetter(true)
|
||||
const shallowReadonlySet = /*#__PURE__*/ createSetter(true, true)
|
||||
const shallowSet = /*#__PURE__*/ createSetter(true)
|
||||
|
||||
function createSetter(isReadonly = false, shallow = false) {
|
||||
function createSetter(shallow = false) {
|
||||
return function set(
|
||||
target: object,
|
||||
key: string | symbol,
|
||||
value: unknown,
|
||||
receiver: object
|
||||
): boolean {
|
||||
if (isReadonly && LOCKED) {
|
||||
if (__DEV__) {
|
||||
console.warn(
|
||||
`Set operation on key "${String(key)}" failed: target is readonly.`,
|
||||
target
|
||||
)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
const oldValue = (target as any)[key]
|
||||
if (!shallow) {
|
||||
value = toRaw(value)
|
||||
@@ -141,30 +137,32 @@ export const mutableHandlers: ProxyHandler<object> = {
|
||||
|
||||
export const readonlyHandlers: ProxyHandler<object> = {
|
||||
get: readonlyGet,
|
||||
set: readonlySet,
|
||||
has,
|
||||
ownKeys,
|
||||
deleteProperty(target: object, key: string | symbol): boolean {
|
||||
if (LOCKED) {
|
||||
if (__DEV__) {
|
||||
console.warn(
|
||||
`Delete operation on key "${String(
|
||||
key
|
||||
)}" failed: target is readonly.`,
|
||||
target
|
||||
)
|
||||
}
|
||||
return true
|
||||
} else {
|
||||
return deleteProperty(target, key)
|
||||
set(target, key) {
|
||||
if (__DEV__) {
|
||||
console.warn(
|
||||
`Set operation on key "${String(key)}" failed: target is readonly.`,
|
||||
target
|
||||
)
|
||||
}
|
||||
return true
|
||||
},
|
||||
deleteProperty(target, key) {
|
||||
if (__DEV__) {
|
||||
console.warn(
|
||||
`Delete operation on key "${String(key)}" failed: target is readonly.`,
|
||||
target
|
||||
)
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
export const shallowReactiveHandlers: ProxyHandler<object> = {
|
||||
...mutableHandlers,
|
||||
get: shallowReactiveGet,
|
||||
set: shallowReactiveSet
|
||||
get: shallowGet,
|
||||
set: shallowSet
|
||||
}
|
||||
|
||||
// Props handlers are special in the sense that it should not unwrap top-level
|
||||
@@ -172,6 +170,5 @@ export const shallowReactiveHandlers: ProxyHandler<object> = {
|
||||
// retain the reactivity of the normal readonly object.
|
||||
export const shallowReadonlyHandlers: ProxyHandler<object> = {
|
||||
...readonlyHandlers,
|
||||
get: shallowReadonlyGet,
|
||||
set: shallowReadonlySet
|
||||
get: shallowReadonlyGet
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { toRaw, reactive, readonly } from './reactive'
|
||||
import { track, trigger, ITERATE_KEY, MAP_KEY_ITERATE_KEY } from './effect'
|
||||
import { TrackOpTypes, TriggerOpTypes } from './operations'
|
||||
import { LOCKED } from './lock'
|
||||
import {
|
||||
isObject,
|
||||
capitalize,
|
||||
@@ -142,7 +141,7 @@ function createForEach(isReadonly: boolean) {
|
||||
const observed = this
|
||||
const target = toRaw(observed)
|
||||
const wrap = isReadonly ? toReadonly : toReactive
|
||||
track(target, TrackOpTypes.ITERATE, ITERATE_KEY)
|
||||
!isReadonly && track(target, TrackOpTypes.ITERATE, ITERATE_KEY)
|
||||
// important: create sure the callback is
|
||||
// 1. invoked with the reactive map as `this` and 3rd arg
|
||||
// 2. the value received should be a corresponding reactive/readonly.
|
||||
@@ -161,11 +160,12 @@ function createIterableMethod(method: string | symbol, isReadonly: boolean) {
|
||||
const isKeyOnly = method === 'keys' && isMap
|
||||
const innerIterator = getProto(target)[method].apply(target, args)
|
||||
const wrap = isReadonly ? toReadonly : toReactive
|
||||
track(
|
||||
target,
|
||||
TrackOpTypes.ITERATE,
|
||||
isKeyOnly ? MAP_KEY_ITERATE_KEY : ITERATE_KEY
|
||||
)
|
||||
!isReadonly &&
|
||||
track(
|
||||
target,
|
||||
TrackOpTypes.ITERATE,
|
||||
isKeyOnly ? MAP_KEY_ITERATE_KEY : ITERATE_KEY
|
||||
)
|
||||
// return a wrapped iterator which returns observed versions of the
|
||||
// values emitted from the real iterator
|
||||
return {
|
||||
@@ -187,23 +187,16 @@ function createIterableMethod(method: string | symbol, isReadonly: boolean) {
|
||||
}
|
||||
}
|
||||
|
||||
function createReadonlyMethod(
|
||||
method: Function,
|
||||
type: TriggerOpTypes
|
||||
): Function {
|
||||
function createReadonlyMethod(type: TriggerOpTypes): Function {
|
||||
return function(this: CollectionTypes, ...args: unknown[]) {
|
||||
if (LOCKED) {
|
||||
if (__DEV__) {
|
||||
const key = args[0] ? `on key "${args[0]}" ` : ``
|
||||
console.warn(
|
||||
`${capitalize(type)} operation ${key}failed: target is readonly.`,
|
||||
toRaw(this)
|
||||
)
|
||||
}
|
||||
return type === TriggerOpTypes.DELETE ? false : this
|
||||
} else {
|
||||
return method.apply(this, args)
|
||||
if (__DEV__) {
|
||||
const key = args[0] ? `on key "${args[0]}" ` : ``
|
||||
console.warn(
|
||||
`${capitalize(type)} operation ${key}failed: target is readonly.`,
|
||||
toRaw(this)
|
||||
)
|
||||
}
|
||||
return type === TriggerOpTypes.DELETE ? false : this
|
||||
}
|
||||
}
|
||||
|
||||
@@ -230,10 +223,10 @@ const readonlyInstrumentations: Record<string, Function> = {
|
||||
return size((this as unknown) as IterableCollections)
|
||||
},
|
||||
has,
|
||||
add: createReadonlyMethod(add, TriggerOpTypes.ADD),
|
||||
set: createReadonlyMethod(set, TriggerOpTypes.SET),
|
||||
delete: createReadonlyMethod(deleteEntry, TriggerOpTypes.DELETE),
|
||||
clear: createReadonlyMethod(clear, TriggerOpTypes.CLEAR),
|
||||
add: createReadonlyMethod(TriggerOpTypes.ADD),
|
||||
set: createReadonlyMethod(TriggerOpTypes.SET),
|
||||
delete: createReadonlyMethod(TriggerOpTypes.DELETE),
|
||||
clear: createReadonlyMethod(TriggerOpTypes.CLEAR),
|
||||
forEach: createForEach(true)
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ const targetMap = new WeakMap<any, KeyToDepMap>()
|
||||
export interface ReactiveEffect<T = any> {
|
||||
(...args: any[]): T
|
||||
_isEffect: true
|
||||
id: number
|
||||
active: boolean
|
||||
raw: () => T
|
||||
deps: Array<Dep>
|
||||
@@ -21,7 +22,7 @@ export interface ReactiveEffect<T = any> {
|
||||
export interface ReactiveEffectOptions {
|
||||
lazy?: boolean
|
||||
computed?: boolean
|
||||
scheduler?: (job: () => void) => void
|
||||
scheduler?: (job: ReactiveEffect) => void
|
||||
onTrack?: (event: DebuggerEvent) => void
|
||||
onTrigger?: (event: DebuggerEvent) => void
|
||||
onStop?: () => void
|
||||
@@ -74,6 +75,8 @@ export function stop(effect: ReactiveEffect) {
|
||||
}
|
||||
}
|
||||
|
||||
let uid = 0
|
||||
|
||||
function createReactiveEffect<T = any>(
|
||||
fn: (...args: any[]) => T,
|
||||
options: ReactiveEffectOptions
|
||||
@@ -96,6 +99,7 @@ function createReactiveEffect<T = any>(
|
||||
}
|
||||
}
|
||||
} as ReactiveEffect
|
||||
effect.id = uid++
|
||||
effect._isEffect = true
|
||||
effect.active = true
|
||||
effect.raw = fn
|
||||
|
||||
@@ -1,4 +1,14 @@
|
||||
export { ref, unref, shallowRef, isRef, toRefs, Ref, UnwrapRef } from './ref'
|
||||
export {
|
||||
ref,
|
||||
unref,
|
||||
shallowRef,
|
||||
isRef,
|
||||
toRef,
|
||||
toRefs,
|
||||
customRef,
|
||||
Ref,
|
||||
UnwrapRef
|
||||
} from './ref'
|
||||
export {
|
||||
reactive,
|
||||
isReactive,
|
||||
@@ -7,7 +17,6 @@ export {
|
||||
isReadonly,
|
||||
shallowReadonly,
|
||||
toRaw,
|
||||
markReadonly,
|
||||
markNonReactive
|
||||
} from './reactive'
|
||||
export {
|
||||
@@ -31,5 +40,4 @@ export {
|
||||
ReactiveEffectOptions,
|
||||
DebuggerEvent
|
||||
} from './effect'
|
||||
export { lock, unlock } from './lock'
|
||||
export { TrackOpTypes, TriggerOpTypes } from './operations'
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
// global immutability lock
|
||||
export let LOCKED = true
|
||||
|
||||
export function lock() {
|
||||
LOCKED = true
|
||||
}
|
||||
|
||||
export function unlock() {
|
||||
LOCKED = false
|
||||
}
|
||||
@@ -2,14 +2,14 @@ import { isObject, toRawType } from '@vue/shared'
|
||||
import {
|
||||
mutableHandlers,
|
||||
readonlyHandlers,
|
||||
shallowReadonlyHandlers,
|
||||
shallowReactiveHandlers
|
||||
shallowReactiveHandlers,
|
||||
shallowReadonlyHandlers
|
||||
} from './baseHandlers'
|
||||
import {
|
||||
mutableCollectionHandlers,
|
||||
readonlyCollectionHandlers
|
||||
} from './collectionHandlers'
|
||||
import { UnwrapRef, Ref, isRef } from './ref'
|
||||
import { UnwrapRef, Ref } from './ref'
|
||||
import { makeMap } from '@vue/shared'
|
||||
|
||||
// WeakMaps that store {raw <-> observed} pairs.
|
||||
@@ -20,7 +20,6 @@ const readonlyToRaw = new WeakMap<any, any>()
|
||||
|
||||
// WeakSets for values that are marked readonly or non-reactive during
|
||||
// observable creation.
|
||||
const readonlyValues = new WeakSet<any>()
|
||||
const nonReactiveValues = new WeakSet<any>()
|
||||
|
||||
const collectionTypes = new Set<Function>([Set, Map, WeakMap, WeakSet])
|
||||
@@ -47,13 +46,6 @@ export function reactive(target: object) {
|
||||
if (readonlyToRaw.has(target)) {
|
||||
return target
|
||||
}
|
||||
// target is explicitly marked as readonly by user
|
||||
if (readonlyValues.has(target)) {
|
||||
return readonly(target)
|
||||
}
|
||||
if (isRef(target)) {
|
||||
return target
|
||||
}
|
||||
return createReactiveObject(
|
||||
target,
|
||||
rawToReactive,
|
||||
@@ -63,14 +55,22 @@ export function reactive(target: object) {
|
||||
)
|
||||
}
|
||||
|
||||
// Return a reactive-copy of the original object, where only the root level
|
||||
// properties are reactive, and does NOT unwrap refs nor recursively convert
|
||||
// returned properties.
|
||||
export function shallowReactive<T extends object>(target: T): T {
|
||||
return createReactiveObject(
|
||||
target,
|
||||
rawToReactive,
|
||||
reactiveToRaw,
|
||||
shallowReactiveHandlers,
|
||||
mutableCollectionHandlers
|
||||
)
|
||||
}
|
||||
|
||||
export function readonly<T extends object>(
|
||||
target: T
|
||||
): Readonly<UnwrapNestedRefs<T>> {
|
||||
// value is a mutable observable, retrieve its original and return
|
||||
// a readonly version.
|
||||
if (reactiveToRaw.has(target)) {
|
||||
target = reactiveToRaw.get(target)
|
||||
}
|
||||
return createReactiveObject(
|
||||
target,
|
||||
rawToReadonly,
|
||||
@@ -96,19 +96,6 @@ export function shallowReadonly<T extends object>(
|
||||
)
|
||||
}
|
||||
|
||||
// Return a reactive-copy of the original object, where only the root level
|
||||
// properties are reactive, and does NOT unwrap refs nor recursively convert
|
||||
// returned properties.
|
||||
export function shallowReactive<T extends object>(target: T): T {
|
||||
return createReactiveObject(
|
||||
target,
|
||||
rawToReactive,
|
||||
reactiveToRaw,
|
||||
shallowReactiveHandlers,
|
||||
mutableCollectionHandlers
|
||||
)
|
||||
}
|
||||
|
||||
function createReactiveObject(
|
||||
target: unknown,
|
||||
toProxy: WeakMap<any, any>,
|
||||
@@ -153,12 +140,8 @@ export function isReadonly(value: unknown): boolean {
|
||||
}
|
||||
|
||||
export function toRaw<T>(observed: T): T {
|
||||
return reactiveToRaw.get(observed) || readonlyToRaw.get(observed) || observed
|
||||
}
|
||||
|
||||
export function markReadonly<T>(value: T): T {
|
||||
readonlyValues.add(value)
|
||||
return value
|
||||
observed = readonlyToRaw.get(observed) || observed
|
||||
return reactiveToRaw.get(observed) || observed
|
||||
}
|
||||
|
||||
export function markNonReactive<T extends object>(value: T): T {
|
||||
|
||||
@@ -70,6 +70,31 @@ export function unref<T>(ref: T): T extends Ref<infer V> ? V : T {
|
||||
return isRef(ref) ? (ref.value as any) : ref
|
||||
}
|
||||
|
||||
export type CustomRefFactory<T> = (
|
||||
track: () => void,
|
||||
trigger: () => void
|
||||
) => {
|
||||
get: () => T
|
||||
set: (value: T) => void
|
||||
}
|
||||
|
||||
export function customRef<T>(factory: CustomRefFactory<T>): Ref<T> {
|
||||
const { get, set } = factory(
|
||||
() => track(r, TrackOpTypes.GET, 'value'),
|
||||
() => trigger(r, TriggerOpTypes.SET, 'value')
|
||||
)
|
||||
const r = {
|
||||
_isRef: true,
|
||||
get value() {
|
||||
return get()
|
||||
},
|
||||
set value(v) {
|
||||
set(v)
|
||||
}
|
||||
}
|
||||
return r as any
|
||||
}
|
||||
|
||||
export function toRefs<T extends object>(
|
||||
object: T
|
||||
): { [K in keyof T]: Ref<T[K]> } {
|
||||
@@ -78,12 +103,12 @@ export function toRefs<T extends object>(
|
||||
}
|
||||
const ret: any = {}
|
||||
for (const key in object) {
|
||||
ret[key] = toProxyRef(object, key)
|
||||
ret[key] = toRef(object, key)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
function toProxyRef<T extends object, K extends keyof T>(
|
||||
export function toRef<T extends object, K extends keyof T>(
|
||||
object: T,
|
||||
key: K
|
||||
): Ref<T[K]> {
|
||||
|
||||
Reference in New Issue
Block a user