const r = ref(false)
const ror = readonly(r)
const obj = reactive({ ror })
- try {
+ expect(() => {
obj.ror = true
- } catch (e) {}
-
+ }).toThrow()
expect(obj.ror).toBe(false)
})
+
test('replacing a readonly ref nested in a reactive object with a new ref', () => {
const r = ref(false)
const ror = readonly(r)
const obj = reactive({ ror })
- try {
- obj.ror = ref(true) as unknown as boolean
- } catch (e) {}
-
+ obj.ror = ref(true) as unknown as boolean
expect(obj.ror).toBe(true)
expect(toRaw(obj).ror).not.toBe(ror) // ref successfully replaced
})
+
+ test('setting readonly object to writable nested ref', () => {
+ const r = ref<any>()
+ const obj = reactive({ r })
+ const ro = readonly({})
+ obj.r = ro
+ expect(obj.r).toBe(ro)
+ expect(r.value).toBe(ro)
+ })
})
} from '../src/index'
import { computed } from '@vue/runtime-dom'
import { shallowRef, unref, customRef, triggerRef } from '../src/ref'
-import { isShallow } from '../src/reactive'
+import { isShallow, readonly, shallowReactive } from '../src/reactive'
describe('reactivity/ref', () => {
it('should hold a value', () => {
b.value = obj
expect(spy2).toBeCalledTimes(1)
})
+
+ test('ref should preserve value shallow/readonly-ness', () => {
+ const original = {}
+ const r = reactive(original)
+ const s = shallowReactive(original)
+ const rr = readonly(original)
+ const a = ref(original)
+
+ expect(a.value).toBe(r)
+
+ a.value = s
+ expect(a.value).toBe(s)
+ expect(a.value).not.toBe(r)
+
+ a.value = rr
+ expect(a.value).toBe(rr)
+ expect(a.value).not.toBe(r)
+ })
})
} from '../src/reactive'
import { effect } from '../src/effect'
+import { Ref, isRef, ref } from '../src/ref'
describe('shallowReactive', () => {
test('should not make non-reactive properties reactive', () => {
expect(isReactive(r.foo.bar)).toBe(false)
})
+ // vuejs/vue#12597
+ test('should not unwrap refs', () => {
+ const foo = shallowReactive({
+ bar: ref(123)
+ })
+ expect(isRef(foo.bar)).toBe(true)
+ expect(foo.bar.value).toBe(123)
+ })
+
+ // vuejs/vue#12688
+ test('should not mutate refs', () => {
+ const original = ref(123)
+ const foo = shallowReactive<{ bar: Ref<number> | number }>({
+ bar: original
+ })
+ expect(foo.bar).toBe(original)
+ foo.bar = 234
+ expect(foo.bar).toBe(234)
+ expect(original.value).toBe(123)
+ })
+
test('should respect shallow/deep versions of same target on access', () => {
const original = {}
const shallow = shallowReactive(original)
if (isReadonly(oldValue) && isRef(oldValue) && !isRef(value)) {
return false
}
- if (!shallow && !isReadonly(value)) {
- if (!isShallow(value)) {
- value = toRaw(value)
+ if (!shallow) {
+ if (!isShallow(value) && !isReadonly(value)) {
oldValue = toRaw(oldValue)
+ value = toRaw(value)
}
if (!isArray(target) && isRef(oldValue) && !isRef(value)) {
oldValue.value = value
} from './effect'
import { TrackOpTypes, TriggerOpTypes } from './operations'
import { isArray, hasChanged, IfAny } from '@vue/shared'
-import { isProxy, toRaw, isReactive, toReactive } from './reactive'
+import {
+ isProxy,
+ toRaw,
+ isReactive,
+ toReactive,
+ isReadonly,
+ isShallow
+} from './reactive'
import type { ShallowReactiveMarker } from './reactive'
import { CollectionTypes } from './collectionHandlers'
import { createDep, Dep } from './dep'
}
set value(newVal) {
- newVal = this.__v_isShallow ? newVal : toRaw(newVal)
+ const useDirectValue =
+ this.__v_isShallow || isShallow(newVal) || isReadonly(newVal)
+ newVal = useDirectValue ? newVal : toRaw(newVal)
if (hasChanged(newVal, this._rawValue)) {
this._rawValue = newVal
- this._value = this.__v_isShallow ? newVal : toReactive(newVal)
+ this._value = useDirectValue ? newVal : toReactive(newVal)
triggerRefValue(this, newVal)
}
}