Prechádzať zdrojové kódy

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

fix #3376
Evan You 5 rokov pred
rodič
commit
41e02f0fac

+ 22 - 1
packages/reactivity/__tests__/readonly.spec.ts

@@ -8,7 +8,8 @@ import {
   effect,
   ref,
   shallowReadonly,
-  isProxy
+  isProxy,
+  computed
 } from '../src'
 
 /**
@@ -435,6 +436,26 @@ describe('reactivity/readonly', () => {
     ).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', () => {
     test('should not make non-reactive properties reactive', () => {
       const props = shallowReadonly({ n: { foo: 1 } })

+ 7 - 5
packages/reactivity/src/computed.ts

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