fix(reactivity): fix mutation on user proxy of reactive Array

close #9742
close #9751
close #9750
This commit is contained in:
Evan You 2023-12-07 13:26:30 +08:00
parent 983d45d4f8
commit 6ecbd5ce2a
3 changed files with 54 additions and 17 deletions

View 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>', () => {

View 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(() => {

View 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)