parent
2dcdbca6e2
commit
0a4f306492
@ -470,5 +470,13 @@ describe('reactivity/readonly', () => {
|
|||||||
`Set operation on key "foo" failed: target is readonly.`
|
`Set operation on key "foo" failed: target is readonly.`
|
||||||
).not.toHaveBeenWarned()
|
).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)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -11,7 +11,11 @@ const builtInSymbols = new Set(
|
|||||||
.filter(isSymbol)
|
.filter(isSymbol)
|
||||||
)
|
)
|
||||||
|
|
||||||
function createGetter(isReadonly: boolean, shallow = false) {
|
const get = createGetter()
|
||||||
|
const readonlyGet = createGetter(true)
|
||||||
|
const shallowReadonlyGet = createGetter(true, true)
|
||||||
|
|
||||||
|
function createGetter(isReadonly = false, shallow = false) {
|
||||||
return function get(target: object, key: string | symbol, receiver: object) {
|
return function get(target: object, key: string | symbol, receiver: object) {
|
||||||
const res = Reflect.get(target, key, receiver)
|
const res = Reflect.get(target, key, receiver)
|
||||||
if (isSymbol(key) && builtInSymbols.has(key)) {
|
if (isSymbol(key) && builtInSymbols.has(key)) {
|
||||||
@ -36,39 +40,60 @@ function createGetter(isReadonly: boolean, shallow = false) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function set(
|
const set = createSetter()
|
||||||
target: object,
|
const readonlySet = createSetter(true)
|
||||||
key: string | symbol,
|
const shallowReadonlySet = createSetter(true, true)
|
||||||
value: unknown,
|
|
||||||
receiver: object
|
function createSetter(isReadonly = false, shallow = false) {
|
||||||
): boolean {
|
return function set(
|
||||||
value = toRaw(value)
|
target: object,
|
||||||
const oldValue = (target as any)[key]
|
key: string | symbol,
|
||||||
if (isRef(oldValue) && !isRef(value)) {
|
value: unknown,
|
||||||
oldValue.value = value
|
receiver: object
|
||||||
return true
|
): boolean {
|
||||||
}
|
if (isReadonly && LOCKED) {
|
||||||
const hadKey = hasOwn(target, key)
|
if (__DEV__) {
|
||||||
const result = Reflect.set(target, key, value, receiver)
|
console.warn(
|
||||||
// don't trigger if target is something up in the prototype chain of original
|
`Set operation on key "${String(key)}" failed: target is readonly.`,
|
||||||
if (target === toRaw(receiver)) {
|
target
|
||||||
/* istanbul ignore else */
|
)
|
||||||
if (__DEV__) {
|
}
|
||||||
const extraInfo = { oldValue, newValue: value }
|
return true
|
||||||
if (!hadKey) {
|
}
|
||||||
trigger(target, TriggerOpTypes.ADD, key, extraInfo)
|
|
||||||
} else if (hasChanged(value, oldValue)) {
|
const oldValue = (target as any)[key]
|
||||||
trigger(target, TriggerOpTypes.SET, key, extraInfo)
|
if (!shallow) {
|
||||||
|
value = toRaw(value)
|
||||||
|
if (isRef(oldValue) && !isRef(value)) {
|
||||||
|
oldValue.value = value
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!hadKey) {
|
// in shallow mode, objects are set as-is regardless of reactive or not
|
||||||
trigger(target, TriggerOpTypes.ADD, key)
|
}
|
||||||
} else if (hasChanged(value, oldValue)) {
|
|
||||||
trigger(target, TriggerOpTypes.SET, key)
|
const hadKey = hasOwn(target, key)
|
||||||
|
const result = Reflect.set(target, key, value, receiver)
|
||||||
|
// don't trigger if target is something up in the prototype chain of original
|
||||||
|
if (target === toRaw(receiver)) {
|
||||||
|
/* istanbul ignore else */
|
||||||
|
if (__DEV__) {
|
||||||
|
const extraInfo = { oldValue, newValue: value }
|
||||||
|
if (!hadKey) {
|
||||||
|
trigger(target, TriggerOpTypes.ADD, key, extraInfo)
|
||||||
|
} else if (hasChanged(value, oldValue)) {
|
||||||
|
trigger(target, TriggerOpTypes.SET, key, extraInfo)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!hadKey) {
|
||||||
|
trigger(target, TriggerOpTypes.ADD, key)
|
||||||
|
} else if (hasChanged(value, oldValue)) {
|
||||||
|
trigger(target, TriggerOpTypes.SET, key)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
return result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function deleteProperty(target: object, key: string | symbol): boolean {
|
function deleteProperty(target: object, key: string | symbol): boolean {
|
||||||
@ -98,7 +123,7 @@ function ownKeys(target: object): (string | number | symbol)[] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const mutableHandlers: ProxyHandler<object> = {
|
export const mutableHandlers: ProxyHandler<object> = {
|
||||||
get: createGetter(false),
|
get,
|
||||||
set,
|
set,
|
||||||
deleteProperty,
|
deleteProperty,
|
||||||
has,
|
has,
|
||||||
@ -106,27 +131,10 @@ export const mutableHandlers: ProxyHandler<object> = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const readonlyHandlers: ProxyHandler<object> = {
|
export const readonlyHandlers: ProxyHandler<object> = {
|
||||||
get: createGetter(true),
|
get: readonlyGet,
|
||||||
|
set: readonlySet,
|
||||||
set(
|
has,
|
||||||
target: object,
|
ownKeys,
|
||||||
key: string | symbol,
|
|
||||||
value: unknown,
|
|
||||||
receiver: object
|
|
||||||
): boolean {
|
|
||||||
if (LOCKED) {
|
|
||||||
if (__DEV__) {
|
|
||||||
console.warn(
|
|
||||||
`Set operation on key "${String(key)}" failed: target is readonly.`,
|
|
||||||
target
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
} else {
|
|
||||||
return set(target, key, value, receiver)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
deleteProperty(target: object, key: string | symbol): boolean {
|
deleteProperty(target: object, key: string | symbol): boolean {
|
||||||
if (LOCKED) {
|
if (LOCKED) {
|
||||||
if (__DEV__) {
|
if (__DEV__) {
|
||||||
@ -141,10 +149,7 @@ export const readonlyHandlers: ProxyHandler<object> = {
|
|||||||
} else {
|
} else {
|
||||||
return deleteProperty(target, key)
|
return deleteProperty(target, key)
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
has,
|
|
||||||
ownKeys
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// props handlers are special in the sense that it should not unwrap top-level
|
// props handlers are special in the sense that it should not unwrap top-level
|
||||||
@ -152,5 +157,6 @@ export const readonlyHandlers: ProxyHandler<object> = {
|
|||||||
// retain the reactivity of the normal readonly object.
|
// retain the reactivity of the normal readonly object.
|
||||||
export const shallowReadonlyHandlers: ProxyHandler<object> = {
|
export const shallowReadonlyHandlers: ProxyHandler<object> = {
|
||||||
...readonlyHandlers,
|
...readonlyHandlers,
|
||||||
get: createGetter(true, true)
|
get: shallowReadonlyGet,
|
||||||
|
set: shallowReadonlySet
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user