From 3d3aba56a37ba8624adfbc07506490fd78a609f3 Mon Sep 17 00:00:00 2001
From: zhiyuanzmj <260480378@qq.com>
Date: Mon, 3 Mar 2025 12:58:11 +0800
Subject: [PATCH 1/4] feat(runtime-vapor): use shallow clone to support
shallowRef in v-for
---
packages/runtime-vapor/__tests__/for.spec.ts | 6 +--
packages/runtime-vapor/src/apiCreateFor.ts | 39 +++++++++++++++++---
2 files changed, 36 insertions(+), 9 deletions(-)
diff --git a/packages/runtime-vapor/__tests__/for.spec.ts b/packages/runtime-vapor/__tests__/for.spec.ts
index 7ba6023b1e9..978750b4107 100644
--- a/packages/runtime-vapor/__tests__/for.spec.ts
+++ b/packages/runtime-vapor/__tests__/for.spec.ts
@@ -410,12 +410,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
@@ -423,7 +423,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 19653cd5daa..7262ba31fa8 100644
--- a/packages/runtime-vapor/src/apiCreateFor.ts
+++ b/packages/runtime-vapor/src/apiCreateFor.ts
@@ -9,7 +9,16 @@ import {
shallowRef,
toReactive,
} 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,
@@ -116,7 +125,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)
@@ -233,6 +242,7 @@ export const createFor = (
moved = true
}
update(
+ source,
(newBlocks[newIndex] = prevBlock),
...getItem(source, newIndex),
)
@@ -320,22 +330,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) {
@@ -403,11 +418,23 @@ function normalizeSource(source: any): ResolvedSource {
return { values, needsWrap, keys }
}
+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 }: ResolvedSource,
idx: number,
): [item: any, key: any, index?: number] {
- const value = needsWrap ? toReactive(values[idx]) : values[idx]
+ const value = needsWrap ? toReactive(values[idx]) : shallowClone(values[idx])
if (keys) {
return [value, keys[idx], idx]
} else {
From 58e18dbdbf5e3287da7d9373f82fdf50b53782a7 Mon Sep 17 00:00:00 2001
From: zhiyuanzmj <260480378@qq.com>
Date: Mon, 3 Mar 2025 20:31:05 +0800
Subject: [PATCH 2/4] fix: e2e-test
---
packages/runtime-vapor/src/apiCreateFor.ts | 15 ++++++++++-----
1 file changed, 10 insertions(+), 5 deletions(-)
diff --git a/packages/runtime-vapor/src/apiCreateFor.ts b/packages/runtime-vapor/src/apiCreateFor.ts
index 7262ba31fa8..8601e5e3d01 100644
--- a/packages/runtime-vapor/src/apiCreateFor.ts
+++ b/packages/runtime-vapor/src/apiCreateFor.ts
@@ -78,6 +78,7 @@ export const createFor = (
// hydrationNode?: Node,
): VaporFragment => {
let isMounted = false
+ let needsWrapCache: boolean
let oldBlocks: ForBlock[] = []
let newBlocks: ForBlock[]
let parent: ParentNode | undefined | null
@@ -92,7 +93,10 @@ export const createFor = (
}
const renderList = () => {
- const source = normalizeSource(src())
+ const source = normalizeSource(src(), needsWrapCache)
+ if (needsWrapCache === undefined) {
+ needsWrapCache = source.needsWrap
+ }
const newLength = source.values.length
const oldLength = oldBlocks.length
newBlocks = new Array(newLength)
@@ -387,13 +391,14 @@ export function createForSlots(
return slots
}
-function normalizeSource(source: any): ResolvedSource {
+function normalizeSource(source: any, needsWrap?: boolean): ResolvedSource {
let values = source
- let needsWrap = false
let keys
if (isArray(source)) {
if (isReactive(source)) {
- needsWrap = !isShallow(source)
+ if (needsWrap === undefined) {
+ needsWrap = !isShallow(source)
+ }
values = shallowReadArray(source)
}
} else if (isString(source)) {
@@ -415,7 +420,7 @@ function normalizeSource(source: any): ResolvedSource {
}
}
}
- return { values, needsWrap, keys }
+ return { values, needsWrap: !!needsWrap, keys }
}
function shallowClone(val: any) {
From eef6b8aef858028191f949abf945bdc84cb1b9d2 Mon Sep 17 00:00:00 2001
From: zhiyuanzmj <260480378@qq.com>
Date: Tue, 4 Mar 2025 09:58:34 +0800
Subject: [PATCH 3/4] feat: use prevNeedsWrap
---
packages/runtime-vapor/src/apiCreateFor.ts | 12 ++++--------
1 file changed, 4 insertions(+), 8 deletions(-)
diff --git a/packages/runtime-vapor/src/apiCreateFor.ts b/packages/runtime-vapor/src/apiCreateFor.ts
index 8601e5e3d01..2a3c559cb3f 100644
--- a/packages/runtime-vapor/src/apiCreateFor.ts
+++ b/packages/runtime-vapor/src/apiCreateFor.ts
@@ -78,7 +78,7 @@ export const createFor = (
// hydrationNode?: Node,
): VaporFragment => {
let isMounted = false
- let needsWrapCache: boolean
+ let prevNeedsWrap: boolean
let oldBlocks: ForBlock[] = []
let newBlocks: ForBlock[]
let parent: ParentNode | undefined | null
@@ -93,10 +93,8 @@ export const createFor = (
}
const renderList = () => {
- const source = normalizeSource(src(), needsWrapCache)
- if (needsWrapCache === undefined) {
- needsWrapCache = source.needsWrap
- }
+ const source = normalizeSource(src(), prevNeedsWrap)
+ prevNeedsWrap = source.needsWrap
const newLength = source.values.length
const oldLength = oldBlocks.length
newBlocks = new Array(newLength)
@@ -396,9 +394,7 @@ function normalizeSource(source: any, needsWrap?: boolean): ResolvedSource {
let keys
if (isArray(source)) {
if (isReactive(source)) {
- if (needsWrap === undefined) {
- needsWrap = !isShallow(source)
- }
+ needsWrap = !isShallow(source)
values = shallowReadArray(source)
}
} else if (isString(source)) {
From f019b7defcd700352a73162935ec60e93caf0cea Mon Sep 17 00:00:00 2001
From: zhiyuanzmj <260480378@qq.com>
Date: Tue, 4 Mar 2025 12:26:38 +0800
Subject: [PATCH 4/4] fix: typo
---
packages/runtime-vapor/src/apiCreateFor.ts | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/packages/runtime-vapor/src/apiCreateFor.ts b/packages/runtime-vapor/src/apiCreateFor.ts
index 2a3c559cb3f..48ce724d975 100644
--- a/packages/runtime-vapor/src/apiCreateFor.ts
+++ b/packages/runtime-vapor/src/apiCreateFor.ts
@@ -389,7 +389,7 @@ export function createForSlots(
return slots
}
-function normalizeSource(source: any, needsWrap?: boolean): ResolvedSource {
+function normalizeSource(source: any, needsWrap = false): ResolvedSource {
let values = source
let keys
if (isArray(source)) {
@@ -416,7 +416,7 @@ function normalizeSource(source: any, needsWrap?: boolean): ResolvedSource {
}
}
}
- return { values, needsWrap: !!needsWrap, keys }
+ return { values, needsWrap, keys }
}
function shallowClone(val: any) {