fix(reactivity): ensure computed can be wrapped by readonly

fix #3376
This commit is contained in:
Evan You 2021-03-25 11:23:26 -04:00
parent e4b5fccd0c
commit 41e02f0fac
2 changed files with 29 additions and 6 deletions

View File

@ -8,7 +8,8 @@ import {
effect, effect,
ref, ref,
shallowReadonly, shallowReadonly,
isProxy isProxy,
computed
} from '../src' } from '../src'
/** /**
@ -435,6 +436,26 @@ describe('reactivity/readonly', () => {
).toHaveBeenWarned() ).toHaveBeenWarned()
}) })
// https://github.com/vuejs/vue-next/issues/3376
test('calling readonly on computed should allow computed to set its private properties', () => {
const r = ref<boolean>(false)
const c = computed(() => r.value)
const rC = readonly(c)
r.value = true
expect(rC.value).toBe(true)
expect(
'Set operation on key "_dirty" failed: target is readonly.'
).not.toHaveBeenWarned()
// @ts-expect-error - non-existant property
rC.randomProperty = true
expect(
'Set operation on key "randomProperty" failed: target is readonly.'
).toHaveBeenWarned()
})
describe('shallowReadonly', () => { describe('shallowReadonly', () => {
test('should not make non-reactive properties reactive', () => { test('should not make non-reactive properties reactive', () => {
const props = shallowReadonly({ n: { foo: 1 } }) const props = shallowReadonly({ n: { foo: 1 } })

View File

@ -48,12 +48,14 @@ class ComputedRefImpl<T> {
} }
get value() { get value() {
if (this._dirty) { // the computed ref may get wrapped by other proxies e.g. readonly() #3376
this._value = this.effect() const self = toRaw(this)
this._dirty = false if (self._dirty) {
self._value = this.effect()
self._dirty = false
} }
track(toRaw(this), TrackOpTypes.GET, 'value') track(self, TrackOpTypes.GET, 'value')
return this._value return self._value
} }
set value(newValue: T) { set value(newValue: T) {