]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(reactivity): fix mutation on user proxy of reactive Array
authorEvan You <yyx990803@gmail.com>
Thu, 7 Dec 2023 05:26:30 +0000 (13:26 +0800)
committerEvan You <yyx990803@gmail.com>
Thu, 7 Dec 2023 05:26:30 +0000 (13:26 +0800)
close #9742
close #9751
close #9750

packages/reactivity/__tests__/reactive.spec.ts
packages/reactivity/__tests__/reactiveArray.spec.ts
packages/reactivity/src/baseHandlers.ts

index e7fe18252ab89eb2712b54b0477e9cef613bddae..1ea1ba4a901732f9127a5ec2b8baf50842a84623 100644 (file)
@@ -158,6 +158,21 @@ describe('reactivity/reactive', () => {
     expect(original.bar).toBe(original2)
   })
 
+  // #1246
+  test('mutation on objects using reactive as prototype should not trigger', () => {
+    const observed = reactive({ foo: 1 })
+    const original = Object.create(observed)
+    let dummy
+    effect(() => (dummy = original.foo))
+    expect(dummy).toBe(1)
+    observed.foo = 2
+    expect(dummy).toBe(2)
+    original.foo = 3
+    expect(dummy).toBe(2)
+    original.foo = 4
+    expect(dummy).toBe(2)
+  })
+
   test('toRaw', () => {
     const original = { foo: 1 }
     const observed = reactive(original)
@@ -166,11 +181,18 @@ describe('reactivity/reactive', () => {
   })
 
   test('toRaw on object using reactive as prototype', () => {
-    const original = reactive({})
-    const obj = Object.create(original)
+    const original = { foo: 1 }
+    const observed = reactive(original)
+    const inherted = Object.create(observed)
+    expect(toRaw(inherted)).toBe(inherted)
+  })
+
+  test('toRaw on user Proxy wrapping reactive', () => {
+    const original = {}
+    const re = reactive(original)
+    const obj = new Proxy(re, {})
     const raw = toRaw(obj)
-    expect(raw).toBe(obj)
-    expect(raw).not.toBe(toRaw(original))
+    expect(raw).toBe(original)
   })
 
   test('should not unwrap Ref<T>', () => {
index 808c5aa5529b717029845f076a03b433dd862dab..301896843955b9f9e0540062e177957c1a09948a 100644 (file)
@@ -142,6 +142,15 @@ describe('reactivity/reactive/Array', () => {
     expect(length).toBe('01')
   })
 
+  // #9742
+  test('mutation on user proxy of reactive Array', () => {
+    const array = reactive<number[]>([])
+    const proxy = new Proxy(array, {})
+    proxy.push(1)
+    expect(array).toHaveLength(1)
+    expect(proxy).toHaveLength(1)
+  })
+
   describe('Array methods w/ refs', () => {
     let original: any[]
     beforeEach(() => {
index fa7ebbb7d75a3139ccc2b63f6937aaa26ac6e0ee..7a73a09acdd5250f4fa0ad4f69ae21fa9ee44af8 100644 (file)
@@ -100,19 +100,25 @@ class BaseReactiveHandler implements ProxyHandler<Target> {
       return isReadonly
     } else if (key === ReactiveFlags.IS_SHALLOW) {
       return shallow
-    } else if (
-      key === ReactiveFlags.RAW &&
-      receiver ===
-        (isReadonly
-          ? shallow
-            ? shallowReadonlyMap
-            : readonlyMap
-          : shallow
-            ? shallowReactiveMap
-            : reactiveMap
-        ).get(target)
-    ) {
-      return target
+    } else if (key === ReactiveFlags.RAW) {
+      if (
+        receiver ===
+          (isReadonly
+            ? shallow
+              ? shallowReadonlyMap
+              : readonlyMap
+            : shallow
+              ? shallowReactiveMap
+              : reactiveMap
+          ).get(target) ||
+        // receiver is not the reactive proxy, but has the same prototype
+        // this means the reciever is a user proxy of the reactive proxy
+        Object.getPrototypeOf(target) === Object.getPrototypeOf(receiver)
+      ) {
+        return target
+      }
+      // early return undefined
+      return
     }
 
     const targetIsArray = isArray(target)