mirror of
https://github.com/vuejs/vue.git
synced 2024-11-21 20:28:54 +00:00
fix(reactivity): use WeakMap for proxy/raw checks, compat with non-extensible objects
fix #12799 close #12798
This commit is contained in:
parent
27eed829cc
commit
4a0d88e46e
@ -17,6 +17,7 @@ import {
|
||||
noop
|
||||
} from '../util/index'
|
||||
import { isReadonly, isRef, TrackOpTypes, TriggerOpTypes } from '../../v3'
|
||||
import { rawMap } from '../../v3/reactivity/reactive'
|
||||
|
||||
const arrayKeys = Object.getOwnPropertyNames(arrayMethods)
|
||||
|
||||
@ -118,7 +119,8 @@ export function observe(
|
||||
(ssrMockReactivity || !isServerRendering()) &&
|
||||
(isArray(value) || isPlainObject(value)) &&
|
||||
Object.isExtensible(value) &&
|
||||
!value.__v_skip /* ReactiveFlags.SKIP */
|
||||
!value.__v_skip /* ReactiveFlags.SKIP */ &&
|
||||
!rawMap.has(value)
|
||||
) {
|
||||
ob = new Observer(value, shallow, ssrMockReactivity)
|
||||
}
|
||||
|
@ -5,10 +5,13 @@ import {
|
||||
isPrimitive,
|
||||
warn,
|
||||
toRawType,
|
||||
isServerRendering
|
||||
isServerRendering,
|
||||
isObject
|
||||
} from 'core/util'
|
||||
import type { Ref, UnwrapRefSimple, RawSymbol } from './ref'
|
||||
|
||||
export const rawMap = new WeakMap()
|
||||
|
||||
export const enum ReactiveFlags {
|
||||
SKIP = '__v_skip',
|
||||
IS_READONLY = '__v_isReadonly',
|
||||
@ -119,7 +122,9 @@ export function toRaw<T>(observed: T): T {
|
||||
export function markRaw<T extends object>(
|
||||
value: T
|
||||
): T & { [RawSymbol]?: true } {
|
||||
def(value, ReactiveFlags.SKIP, true)
|
||||
if (isObject(value)) {
|
||||
rawMap.set(value, true)
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
|
@ -32,8 +32,8 @@ export type DeepReadonly<T> = T extends Builtin
|
||||
? { readonly [K in keyof T]: DeepReadonly<T[K]> }
|
||||
: Readonly<T>
|
||||
|
||||
const rawToReadonlyFlag = `__v_rawToReadonly`
|
||||
const rawToShallowReadonlyFlag = `__v_rawToShallowReadonly`
|
||||
const rawToReadonlyMap = new WeakMap()
|
||||
const rawToShallowReadonlyMap = new WeakMap()
|
||||
|
||||
export function readonly<T extends object>(
|
||||
target: T
|
||||
@ -63,14 +63,14 @@ function createReadonly(target: any, shallow: boolean) {
|
||||
}
|
||||
|
||||
// already has a readonly proxy
|
||||
const existingFlag = shallow ? rawToShallowReadonlyFlag : rawToReadonlyFlag
|
||||
const existingProxy = target[existingFlag]
|
||||
const map = shallow ? rawToShallowReadonlyMap : rawToReadonlyMap
|
||||
const existingProxy = map.get(target)
|
||||
if (existingProxy) {
|
||||
return existingProxy
|
||||
}
|
||||
|
||||
const proxy = Object.create(Object.getPrototypeOf(target))
|
||||
def(target, existingFlag, proxy)
|
||||
map.set(target, proxy)
|
||||
|
||||
def(proxy, ReactiveFlags.IS_READONLY, true)
|
||||
def(proxy, ReactiveFlags.RAW, target)
|
||||
|
@ -258,6 +258,12 @@ describe('reactivity/reactive', () => {
|
||||
expect(isReactive(obj.bar)).toBe(false)
|
||||
})
|
||||
|
||||
test('markRaw on non-extensible objects', () => {
|
||||
const foo = Object.freeze({})
|
||||
markRaw(foo)
|
||||
expect(isReactive(reactive(foo))).toBe(false)
|
||||
})
|
||||
|
||||
test('should not observe non-extensible objects', () => {
|
||||
const obj = reactive({
|
||||
foo: Object.preventExtensions({ a: 1 }),
|
||||
|
@ -525,4 +525,11 @@ describe('reactivity/readonly', () => {
|
||||
expect(readonlyFoo.x).toBe(1)
|
||||
expect(`et operation on key "x" failed`).toHaveBeenWarned()
|
||||
})
|
||||
|
||||
test('compatibility with non-extensible objects', () => {
|
||||
const foo = Object.freeze({ a: 1 })
|
||||
const bar = readonly(foo)
|
||||
expect(isReadonly(bar)).toBe(true)
|
||||
expect(readonly(foo)).toBe(bar)
|
||||
})
|
||||
})
|
||||
|
Loading…
Reference in New Issue
Block a user