mirror of https://github.com/vuejs/core.git
fix(runtime-core): respect immutability for readonly reactive arrays in `v-for` (#13091)
close #13087
This commit is contained in:
parent
9196222ae1
commit
3f27c58ffb
|
@ -1,4 +1,10 @@
|
||||||
import { isReactive, reactive, shallowReactive } from '../../src/index'
|
import {
|
||||||
|
effect,
|
||||||
|
isReactive,
|
||||||
|
reactive,
|
||||||
|
readonly,
|
||||||
|
shallowReactive,
|
||||||
|
} from '../../src/index'
|
||||||
import { renderList } from '../../src/helpers/renderList'
|
import { renderList } from '../../src/helpers/renderList'
|
||||||
|
|
||||||
describe('renderList', () => {
|
describe('renderList', () => {
|
||||||
|
@ -65,4 +71,31 @@ describe('renderList', () => {
|
||||||
const shallowReactiveArray = shallowReactive([{ foo: 1 }])
|
const shallowReactiveArray = shallowReactive([{ foo: 1 }])
|
||||||
expect(renderList(shallowReactiveArray, isReactive)).toEqual([false])
|
expect(renderList(shallowReactiveArray, isReactive)).toEqual([false])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should not allow mutation', () => {
|
||||||
|
const arr = readonly(reactive([{ foo: 1 }]))
|
||||||
|
expect(
|
||||||
|
renderList(arr, item => {
|
||||||
|
;(item as any).foo = 0
|
||||||
|
return item.foo
|
||||||
|
}),
|
||||||
|
).toEqual([1])
|
||||||
|
expect(
|
||||||
|
`Set operation on key "foo" failed: target is readonly.`,
|
||||||
|
).toHaveBeenWarned()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should trigger effect for deep mutations in readonly reactive arrays', () => {
|
||||||
|
const arr = reactive([{ foo: 1 }])
|
||||||
|
const readonlyArr = readonly(arr)
|
||||||
|
|
||||||
|
let dummy
|
||||||
|
effect(() => {
|
||||||
|
dummy = renderList(readonlyArr, item => item.foo)
|
||||||
|
})
|
||||||
|
expect(dummy).toEqual([1])
|
||||||
|
|
||||||
|
arr[0].foo = 2
|
||||||
|
expect(dummy).toEqual([2])
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
import type { VNode, VNodeChild } from '../vnode'
|
import type { VNode, VNodeChild } from '../vnode'
|
||||||
import {
|
import {
|
||||||
isReactive,
|
isReactive,
|
||||||
|
isReadonly,
|
||||||
isShallow,
|
isShallow,
|
||||||
shallowReadArray,
|
shallowReadArray,
|
||||||
toReactive,
|
toReactive,
|
||||||
|
toReadonly,
|
||||||
} from '@vue/reactivity'
|
} from '@vue/reactivity'
|
||||||
import { isArray, isObject, isString } from '@vue/shared'
|
import { isArray, isObject, isString } from '@vue/shared'
|
||||||
import { warn } from '../warning'
|
import { warn } from '../warning'
|
||||||
|
@ -69,14 +71,20 @@ export function renderList(
|
||||||
if (sourceIsArray || isString(source)) {
|
if (sourceIsArray || isString(source)) {
|
||||||
const sourceIsReactiveArray = sourceIsArray && isReactive(source)
|
const sourceIsReactiveArray = sourceIsArray && isReactive(source)
|
||||||
let needsWrap = false
|
let needsWrap = false
|
||||||
|
let isReadonlySource = false
|
||||||
if (sourceIsReactiveArray) {
|
if (sourceIsReactiveArray) {
|
||||||
needsWrap = !isShallow(source)
|
needsWrap = !isShallow(source)
|
||||||
|
isReadonlySource = isReadonly(source)
|
||||||
source = shallowReadArray(source)
|
source = shallowReadArray(source)
|
||||||
}
|
}
|
||||||
ret = new Array(source.length)
|
ret = new Array(source.length)
|
||||||
for (let i = 0, l = source.length; i < l; i++) {
|
for (let i = 0, l = source.length; i < l; i++) {
|
||||||
ret[i] = renderItem(
|
ret[i] = renderItem(
|
||||||
needsWrap ? toReactive(source[i]) : source[i],
|
needsWrap
|
||||||
|
? isReadonlySource
|
||||||
|
? toReadonly(toReactive(source[i]))
|
||||||
|
: toReactive(source[i])
|
||||||
|
: source[i],
|
||||||
i,
|
i,
|
||||||
undefined,
|
undefined,
|
||||||
cached && cached[i],
|
cached && cached[i],
|
||||||
|
|
Loading…
Reference in New Issue