diff --git a/packages/runtime-vapor/__tests__/for.spec.ts b/packages/runtime-vapor/__tests__/for.spec.ts
index db91b6a62da..ca5b7f30364 100644
--- a/packages/runtime-vapor/__tests__/for.spec.ts
+++ b/packages/runtime-vapor/__tests__/for.spec.ts
@@ -417,12 +417,12 @@ describe('createFor', () => {
'
0. 11. 22. 33. 4',
)
- // change deep value should not update
+ // change
list.value[0].name = 'a'
setList()
await nextTick()
expect(host.innerHTML).toBe(
- '0. 11. 22. 33. 4',
+ '0. a1. 22. 33. 4',
)
// remove
@@ -430,7 +430,7 @@ describe('createFor', () => {
setList()
await nextTick()
expect(host.innerHTML).toBe(
- '0. 11. 32. 4',
+ '0. a1. 32. 4',
)
// clear
diff --git a/packages/runtime-vapor/src/apiCreateFor.ts b/packages/runtime-vapor/src/apiCreateFor.ts
index 62529149ad4..f6a694714d5 100644
--- a/packages/runtime-vapor/src/apiCreateFor.ts
+++ b/packages/runtime-vapor/src/apiCreateFor.ts
@@ -11,7 +11,16 @@ import {
toReactive,
toReadonly,
} from '@vue/reactivity'
-import { getSequence, isArray, isObject, isString } from '@vue/shared'
+import {
+ extend,
+ getSequence,
+ isArray,
+ isMap,
+ isObject,
+ isSet,
+ isString,
+ looseEqual,
+} from '@vue/shared'
import { createComment, createTextNode } from './dom/node'
import {
type Block,
@@ -84,6 +93,7 @@ export const createFor = (
}
let isMounted = false
+ let prevNeedsWrap: boolean
let oldBlocks: ForBlock[] = []
let newBlocks: ForBlock[]
let parent: ParentNode | undefined | null
@@ -99,7 +109,8 @@ export const createFor = (
}
const renderList = () => {
- const source = normalizeSource(src())
+ const source = normalizeSource(src(), prevNeedsWrap)
+ prevNeedsWrap = source.needsWrap
const newLength = source.values.length
const oldLength = oldBlocks.length
newBlocks = new Array(newLength)
@@ -132,7 +143,7 @@ export const createFor = (
// unkeyed fast path
const commonLength = Math.min(newLength, oldLength)
for (let i = 0; i < commonLength; i++) {
- update((newBlocks[i] = oldBlocks[i]), getItem(source, i)[0])
+ update(source, (newBlocks[i] = oldBlocks[i]), getItem(source, i)[0])
}
for (let i = oldLength; i < newLength; i++) {
mount(source, i)
@@ -249,6 +260,7 @@ export const createFor = (
moved = true
}
update(
+ source,
(newBlocks[newIndex] = prevBlock),
...getItem(source, newIndex),
)
@@ -336,22 +348,27 @@ export const createFor = (
return block
}
- const tryPatchIndex = (source: any, idx: number) => {
+ const tryPatchIndex = (source: ResolvedSource, idx: number) => {
const block = oldBlocks[idx]
const [item, key, index] = getItem(source, idx)
if (block.key === getKey!(item, key, index)) {
- update((newBlocks[idx] = block), item)
+ update(source, (newBlocks[idx] = block), item)
return true
}
}
const update = (
+ { needsWrap }: ResolvedSource,
{ itemRef, keyRef, indexRef }: ForBlock,
newItem: any,
newKey?: any,
newIndex?: any,
) => {
- if (newItem !== itemRef.value) {
+ if (
+ needsWrap
+ ? newItem !== itemRef.value
+ : !looseEqual(newItem, itemRef.value)
+ ) {
itemRef.value = newItem
}
if (keyRef && newKey !== undefined && newKey !== keyRef.value) {
@@ -393,9 +410,8 @@ export function createForSlots(
return slots
}
-function normalizeSource(source: any): ResolvedSource {
+function normalizeSource(source: any, needsWrap = false): ResolvedSource {
let values = source
- let needsWrap = false
let isReadonlySource = false
let keys
if (isArray(source)) {
@@ -431,6 +447,18 @@ function normalizeSource(source: any): ResolvedSource {
}
}
+function shallowClone(val: any) {
+ return Array.isArray(val)
+ ? val.slice()
+ : isObject(val)
+ ? extend({}, val)
+ : isMap(val)
+ ? new Map(val)
+ : isSet(val)
+ ? new Set(val)
+ : val
+}
+
function getItem(
{ keys, values, needsWrap, isReadonlySource }: ResolvedSource,
idx: number,
@@ -439,7 +467,7 @@ function getItem(
? isReadonlySource
? toReadonly(toReactive(values[idx]))
: toReactive(values[idx])
- : values[idx]
+ : shallowClone(values[idx])
if (keys) {
return [value, keys[idx], idx]
} else {