@@ -11,7 +11,7 @@ import {
11
11
toReactive ,
12
12
watch ,
13
13
} from '@vue/reactivity'
14
- import { getSequence , isArray , isObject , isString } from '@vue/shared'
14
+ import { isArray , isObject , isString } from '@vue/shared'
15
15
import { createComment , createTextNode } from './dom/node'
16
16
import {
17
17
type Block ,
@@ -134,149 +134,90 @@ export const createFor = (
134
134
unmount ( oldBlocks [ i ] )
135
135
}
136
136
} 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
137
+ const commonLength = Math . min ( oldLength , newLength )
138
+ const oldKeyToIndexMap = new Map < any , number > ( )
139
+ const pendingNews : [
140
+ index : number ,
141
+ item : ReturnType < typeof getItem > ,
142
+ key : any ,
143
+ ] [ ] = [ ]
144
+
145
+ let defaultAnchor : Node = parentAnchor
146
+ let right = 0
147
+ let left = 0
148
+
149
+ while ( right < commonLength ) {
150
+ const index = newLength - right - 1
151
+ const item = getItem ( source , index )
152
+ const key = getKey . apply ( null , item )
153
+ const block = oldBlocks [ oldLength - right - 1 ]
154
+ if ( block . key === key ) {
155
+ update ( block , ...item )
156
+ newBlocks [ index ] = block
157
+ right ++
158
+ continue
159
+ }
160
+ if ( right !== 0 ) {
161
+ defaultAnchor = normalizeAnchor ( newBlocks [ index + 1 ] . nodes )
149
162
}
163
+ break
150
164
}
151
165
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 --
166
+ while ( left < commonLength - right ) {
167
+ const item = getItem ( source , left )
168
+ const key = getKey . apply ( null , item )
169
+ const oldBlock = oldBlocks [ left ]
170
+ const oldKey = oldBlock . key
171
+ if ( oldKey === key ) {
172
+ update ( ( newBlocks [ left ] = oldBlock ) , item [ 0 ] )
159
173
} else {
160
- break
174
+ pendingNews . push ( [ left , item , key ] )
175
+ oldKeyToIndexMap . set ( oldKey , left )
161
176
}
177
+ left ++
162
178
}
163
179
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
- }
180
+ for ( let i = left ; i < oldLength - right ; i ++ ) {
181
+ oldKeyToIndexMap . set ( oldBlocks [ i ] . key , i )
183
182
}
184
183
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 ++
184
+ const moveOrMount = (
185
+ index : number ,
186
+ item : ReturnType < typeof getItem > ,
187
+ key : any ,
188
+ anchor : Node ,
189
+ ) => {
190
+ const oldIndex = oldKeyToIndexMap . get ( key )
191
+ if ( oldIndex !== undefined ) {
192
+ const block = ( newBlocks [ index ] = oldBlocks [ oldIndex ] )
193
+ update ( block , ...item )
194
+ insert ( block , parent ! , anchor )
195
+ oldKeyToIndexMap . delete ( key )
196
+ } else {
197
+ mount ( source , index , item , key , anchor )
196
198
}
197
199
}
198
200
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
- }
201
+ for ( let i = pendingNews . length - 1 ; i >= 0 ; i -- ) {
202
+ const [ index , item , key ] = pendingNews [ i ]
203
+ moveOrMount (
204
+ index ,
205
+ item ,
206
+ key ,
207
+ index < commonLength - 1
208
+ ? normalizeAnchor ( newBlocks [ index + 1 ] . nodes )
209
+ : defaultAnchor ,
210
+ )
211
+ }
212
212
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
- }
213
+ for ( let i = left ; i < newLength - right ; i ++ ) {
214
+ const item = getItem ( source , i )
215
+ const key = getKey . apply ( null , item )
216
+ moveOrMount ( i , item , key , defaultAnchor )
217
+ }
252
218
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
- }
219
+ for ( const i of oldKeyToIndexMap . values ( ) ) {
220
+ unmount ( oldBlocks [ i ] )
280
221
}
281
222
}
282
223
}
@@ -295,9 +236,10 @@ export const createFor = (
295
236
const mount = (
296
237
source : ResolvedSource ,
297
238
idx : number ,
239
+ [ item , key , index ] = getItem ( source , idx ) ,
240
+ key2 = getKey && getKey ( item , key , index ) ,
298
241
anchor : Node | undefined = parentAnchor ,
299
242
) : ForBlock => {
300
- const [ item , key , index ] = getItem ( source , idx )
301
243
const itemRef = shallowRef ( item )
302
244
// avoid creating refs if the render fn doesn't need it
303
245
const keyRef = needKey ? shallowRef ( key ) : undefined
@@ -321,23 +263,14 @@ export const createFor = (
321
263
itemRef ,
322
264
keyRef ,
323
265
indexRef ,
324
- getKey && getKey ( item , key , index ) ,
266
+ key2 ,
325
267
) )
326
268
327
269
if ( parent ) insert ( block . nodes , parent , anchor )
328
270
329
271
return block
330
272
}
331
273
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
-
341
274
const update = (
342
275
{ itemRef, keyRef, indexRef } : ForBlock ,
343
276
newItem : any ,
0 commit comments