Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 543785d

Browse files
committedApr 13, 2025·
wip
1 parent a893681 commit 543785d

File tree

2 files changed

+57
-144
lines changed

2 files changed

+57
-144
lines changed
 

Diff for: ‎packages/runtime-vapor/__tests__/for.spec.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ describe('createFor', () => {
9494
})
9595
return span
9696
},
97-
item => item.name,
97+
item => item,
9898
)
9999
return n1
100100
}).render()

Diff for: ‎packages/runtime-vapor/src/apiCreateFor.ts

+56-143
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {
1111
toReactive,
1212
watch,
1313
} from '@vue/reactivity'
14-
import { getSequence, isArray, isObject, isString } from '@vue/shared'
14+
import { isArray, isObject, isString } from '@vue/shared'
1515
import { createComment, createTextNode } from './dom/node'
1616
import {
1717
type Block,
@@ -134,149 +134,70 @@ export const createFor = (
134134
unmount(oldBlocks[i])
135135
}
136136
} else {
137-
let i = 0
138-
let e1 = oldLength - 1 // prev ending index
139-
let e2 = newLength - 1 // next ending index
140-
141-
// 1. sync from start
142-
// (a b) c
143-
// (a b) d e
144-
while (i <= e1 && i <= e2) {
145-
if (tryPatchIndex(source, i)) {
146-
i++
147-
} else {
148-
break
149-
}
150-
}
137+
const commonLength = Math.min(oldLength, newLength)
138+
const unresolvedOlds = new Map<any, number>()
139+
const unresolvedNews: [
140+
index: number,
141+
item: ReturnType<typeof getItem>,
142+
key: any,
143+
][] = []
151144

152-
// 2. sync from end
153-
// a (b c)
154-
// d e (b c)
155-
while (i <= e1 && i <= e2) {
156-
if (tryPatchIndex(source, i)) {
157-
e1--
158-
e2--
145+
for (let i = 0; i < commonLength; i++) {
146+
const item = getItem(source, i)
147+
const key = getKey.apply(null, item)
148+
const oldBlock = oldBlocks[i]
149+
const oldKey = oldBlock.key
150+
if (oldKey === key) {
151+
update((newBlocks[i] = oldBlock), item[0])
159152
} else {
160-
break
153+
unresolvedNews.push([i, item, key])
154+
unresolvedOlds.set(oldKey, i)
161155
}
162156
}
163157

164-
// 3. common sequence + mount
165-
// (a b)
166-
// (a b) c
167-
// i = 2, e1 = 1, e2 = 2
168-
// (a b)
169-
// c (a b)
170-
// i = 0, e1 = -1, e2 = 0
171-
if (i > e1) {
172-
if (i <= e2) {
173-
const nextPos = e2 + 1
174-
const anchor =
175-
nextPos < newLength
176-
? normalizeAnchor(newBlocks[nextPos].nodes)
177-
: parentAnchor
178-
while (i <= e2) {
179-
mount(source, i, anchor)
180-
i++
181-
}
182-
}
158+
for (let i = commonLength; i < oldLength; i++) {
159+
const oldBlock = oldBlocks[i]
160+
unresolvedOlds.set(oldBlock.key, i)
183161
}
184162

185-
// 4. common sequence + unmount
186-
// (a b) c
187-
// (a b)
188-
// i = 2, e1 = 2, e2 = 1
189-
// a (b c)
190-
// (b c)
191-
// i = 0, e1 = 0, e2 = -1
192-
else if (i > e2) {
193-
while (i <= e1) {
194-
unmount(oldBlocks[i])
195-
i++
163+
const unresolvedLength = unresolvedNews.length
164+
const moveOrMount = (
165+
index: number,
166+
item: ReturnType<typeof getItem>,
167+
key: any,
168+
anchor: Node,
169+
) => {
170+
const oldIndex = unresolvedOlds.get(key)
171+
if (oldIndex !== undefined) {
172+
const block = (newBlocks[index] = oldBlocks[oldIndex])
173+
update(block, ...item)
174+
insert(block, parent!, anchor)
175+
unresolvedOlds.delete(key)
176+
} else {
177+
mount(source, index, item, key, anchor)
196178
}
197179
}
198180

199-
// 5. unknown sequence
200-
// [i ... e1 + 1]: a b [c d e] f g
201-
// [i ... e2 + 1]: a b [e d c h] f g
202-
// i = 2, e1 = 4, e2 = 5
203-
else {
204-
const s1 = i // prev starting index
205-
const s2 = i // next starting index
206-
207-
// 5.1 build key:index map for newChildren
208-
const keyToNewIndexMap = new Map()
209-
for (i = s2; i <= e2; i++) {
210-
keyToNewIndexMap.set(getKey(...getItem(source, i)), i)
211-
}
181+
for (let i = unresolvedLength - 1; i >= 0; i--) {
182+
const [index, item, key] = unresolvedNews[i]
183+
moveOrMount(
184+
index,
185+
item,
186+
key,
187+
index < commonLength - 1
188+
? normalizeAnchor(newBlocks[index + 1].nodes)
189+
: parentAnchor,
190+
)
191+
}
212192

213-
// 5.2 loop through old children left to be patched and try to patch
214-
// matching nodes & remove nodes that are no longer present
215-
let j
216-
let patched = 0
217-
const toBePatched = e2 - s2 + 1
218-
let moved = false
219-
// used to track whether any node has moved
220-
let maxNewIndexSoFar = 0
221-
// works as Map<newIndex, oldIndex>
222-
// Note that oldIndex is offset by +1
223-
// and oldIndex = 0 is a special value indicating the new node has
224-
// no corresponding old node.
225-
// used for determining longest stable subsequence
226-
const newIndexToOldIndexMap = new Array(toBePatched).fill(0)
227-
228-
for (i = s1; i <= e1; i++) {
229-
const prevBlock = oldBlocks[i]
230-
if (patched >= toBePatched) {
231-
// all new children have been patched so this can only be a removal
232-
unmount(prevBlock)
233-
} else {
234-
const newIndex = keyToNewIndexMap.get(prevBlock.key)
235-
if (newIndex == null) {
236-
unmount(prevBlock)
237-
} else {
238-
newIndexToOldIndexMap[newIndex - s2] = i + 1
239-
if (newIndex >= maxNewIndexSoFar) {
240-
maxNewIndexSoFar = newIndex
241-
} else {
242-
moved = true
243-
}
244-
update(
245-
(newBlocks[newIndex] = prevBlock),
246-
...getItem(source, newIndex),
247-
)
248-
patched++
249-
}
250-
}
251-
}
193+
for (let index = commonLength; index < newLength; index++) {
194+
const item = getItem(source, index)
195+
const key = getKey.apply(null, item)
196+
moveOrMount(index, item, key, parentAnchor)
197+
}
252198

253-
// 5.3 move and mount
254-
// generate longest stable subsequence only when nodes have moved
255-
const increasingNewIndexSequence = moved
256-
? getSequence(newIndexToOldIndexMap)
257-
: []
258-
j = increasingNewIndexSequence.length - 1
259-
// looping backwards so that we can use last patched node as anchor
260-
for (i = toBePatched - 1; i >= 0; i--) {
261-
const nextIndex = s2 + i
262-
const anchor =
263-
nextIndex + 1 < newLength
264-
? normalizeAnchor(newBlocks[nextIndex + 1].nodes)
265-
: parentAnchor
266-
if (newIndexToOldIndexMap[i] === 0) {
267-
// mount new
268-
mount(source, nextIndex, anchor)
269-
} else if (moved) {
270-
// move if:
271-
// There is no stable subsequence (e.g. a reverse)
272-
// OR current node is not among the stable sequence
273-
if (j < 0 || i !== increasingNewIndexSequence[j]) {
274-
insert(newBlocks[nextIndex].nodes, parent!, anchor)
275-
} else {
276-
j--
277-
}
278-
}
279-
}
199+
for (const i of unresolvedOlds.values()) {
200+
unmount(oldBlocks[i])
280201
}
281202
}
282203
}
@@ -295,9 +216,10 @@ export const createFor = (
295216
const mount = (
296217
source: ResolvedSource,
297218
idx: number,
219+
[item, key, index] = getItem(source, idx),
220+
key2 = getKey && getKey(item, key, index),
298221
anchor: Node | undefined = parentAnchor,
299222
): ForBlock => {
300-
const [item, key, index] = getItem(source, idx)
301223
const itemRef = shallowRef(item)
302224
// avoid creating refs if the render fn doesn't need it
303225
const keyRef = needKey ? shallowRef(key) : undefined
@@ -321,23 +243,14 @@ export const createFor = (
321243
itemRef,
322244
keyRef,
323245
indexRef,
324-
getKey && getKey(item, key, index),
246+
key2,
325247
))
326248

327249
if (parent) insert(block.nodes, parent, anchor)
328250

329251
return block
330252
}
331253

332-
const tryPatchIndex = (source: any, idx: number) => {
333-
const block = oldBlocks[idx]
334-
const [item, key, index] = getItem(source, idx)
335-
if (block.key === getKey!(item, key, index)) {
336-
update((newBlocks[idx] = block), item)
337-
return true
338-
}
339-
}
340-
341254
const update = (
342255
{ itemRef, keyRef, indexRef }: ForBlock,
343256
newItem: any,

0 commit comments

Comments
 (0)
Please sign in to comment.