mirror of https://github.com/vuejs/core.git
fix(runtime-vapor): respect immutability for readonly reactive arrays in `v-for` (#13187)
This commit is contained in:
parent
a0c42ffbbc
commit
7d84010c0f
|
@ -4,7 +4,14 @@ import {
|
|||
getRestElement,
|
||||
renderEffect,
|
||||
} from '../src'
|
||||
import { nextTick, ref, shallowRef, triggerRef } from '@vue/runtime-dom'
|
||||
import {
|
||||
nextTick,
|
||||
reactive,
|
||||
readonly,
|
||||
ref,
|
||||
shallowRef,
|
||||
triggerRef,
|
||||
} from '@vue/runtime-dom'
|
||||
import { makeRender } from './_utils'
|
||||
|
||||
const define = makeRender()
|
||||
|
@ -674,4 +681,57 @@ describe('createFor', () => {
|
|||
await nextTick()
|
||||
expectCalledTimesToBe('Clear rows', 1, 0, 0, 0)
|
||||
})
|
||||
|
||||
describe('readonly source', () => {
|
||||
test('should not allow mutation', () => {
|
||||
const arr = readonly(reactive([{ foo: 1 }]))
|
||||
|
||||
const { host } = define(() => {
|
||||
const n1 = createFor(
|
||||
() => arr,
|
||||
(item, key, index) => {
|
||||
const span = document.createElement('li')
|
||||
renderEffect(() => {
|
||||
item.value.foo = 0
|
||||
span.innerHTML = `${item.value.foo}`
|
||||
})
|
||||
return span
|
||||
},
|
||||
idx => idx,
|
||||
)
|
||||
return n1
|
||||
}).render()
|
||||
|
||||
expect(host.innerHTML).toBe('<li>1</li><!--for-->')
|
||||
expect(
|
||||
`Set operation on key "foo" failed: target is readonly.`,
|
||||
).toHaveBeenWarned()
|
||||
})
|
||||
|
||||
test('should trigger effect for deep mutations', async () => {
|
||||
const arr = reactive([{ foo: 1 }])
|
||||
const readonlyArr = readonly(arr)
|
||||
|
||||
const { host } = define(() => {
|
||||
const n1 = createFor(
|
||||
() => readonlyArr,
|
||||
(item, key, index) => {
|
||||
const span = document.createElement('li')
|
||||
renderEffect(() => {
|
||||
span.innerHTML = `${item.value.foo}`
|
||||
})
|
||||
return span
|
||||
},
|
||||
idx => idx,
|
||||
)
|
||||
return n1
|
||||
}).render()
|
||||
|
||||
expect(host.innerHTML).toBe('<li>1</li><!--for-->')
|
||||
|
||||
arr[0].foo = 2
|
||||
await nextTick()
|
||||
expect(host.innerHTML).toBe('<li>2</li><!--for-->')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -2,12 +2,14 @@ import {
|
|||
EffectScope,
|
||||
type ShallowRef,
|
||||
isReactive,
|
||||
isReadonly,
|
||||
isShallow,
|
||||
pauseTracking,
|
||||
resetTracking,
|
||||
shallowReadArray,
|
||||
shallowRef,
|
||||
toReactive,
|
||||
toReadonly,
|
||||
} from '@vue/reactivity'
|
||||
import { getSequence, isArray, isObject, isString } from '@vue/shared'
|
||||
import { createComment, createTextNode } from './dom/node'
|
||||
|
@ -59,6 +61,7 @@ type Source = any[] | Record<any, any> | number | Set<any> | Map<any, any>
|
|||
type ResolvedSource = {
|
||||
values: any[]
|
||||
needsWrap: boolean
|
||||
isReadonlySource: boolean
|
||||
keys?: string[]
|
||||
}
|
||||
|
||||
|
@ -393,11 +396,13 @@ export function createForSlots(
|
|||
function normalizeSource(source: any): ResolvedSource {
|
||||
let values = source
|
||||
let needsWrap = false
|
||||
let isReadonlySource = false
|
||||
let keys
|
||||
if (isArray(source)) {
|
||||
if (isReactive(source)) {
|
||||
needsWrap = !isShallow(source)
|
||||
values = shallowReadArray(source)
|
||||
isReadonlySource = isReadonly(source)
|
||||
}
|
||||
} else if (isString(source)) {
|
||||
values = source.split('')
|
||||
|
@ -418,14 +423,23 @@ function normalizeSource(source: any): ResolvedSource {
|
|||
}
|
||||
}
|
||||
}
|
||||
return { values, needsWrap, keys }
|
||||
return {
|
||||
values,
|
||||
needsWrap,
|
||||
isReadonlySource,
|
||||
keys,
|
||||
}
|
||||
}
|
||||
|
||||
function getItem(
|
||||
{ keys, values, needsWrap }: ResolvedSource,
|
||||
{ keys, values, needsWrap, isReadonlySource }: ResolvedSource,
|
||||
idx: number,
|
||||
): [item: any, key: any, index?: number] {
|
||||
const value = needsWrap ? toReactive(values[idx]) : values[idx]
|
||||
const value = needsWrap
|
||||
? isReadonlySource
|
||||
? toReadonly(toReactive(values[idx]))
|
||||
: toReactive(values[idx])
|
||||
: values[idx]
|
||||
if (keys) {
|
||||
return [value, keys[idx], idx]
|
||||
} else {
|
||||
|
|
Loading…
Reference in New Issue