@@ -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'
1515import { createComment , createTextNode } from './dom/node'
1616import {
1717 type Block ,
@@ -134,149 +134,90 @@ 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
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 )
149162 }
163+ break
150164 }
151165
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 ] )
159173 } else {
160- break
174+ pendingNews . push ( [ left , item , key ] )
175+ oldKeyToIndexMap . set ( oldKey , left )
161176 }
177+ left ++
162178 }
163179
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 )
183182 }
184183
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 )
196198 }
197199 }
198200
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+ }
212212
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+ }
252218
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 ] )
280221 }
281222 }
282223 }
@@ -295,9 +236,10 @@ export const createFor = (
295236 const mount = (
296237 source : ResolvedSource ,
297238 idx : number ,
239+ [ item , key , index ] = getItem ( source , idx ) ,
240+ key2 = getKey && getKey ( item , key , index ) ,
298241 anchor : Node | undefined = parentAnchor ,
299242 ) : ForBlock => {
300- const [ item , key , index ] = getItem ( source , idx )
301243 const itemRef = shallowRef ( item )
302244 // avoid creating refs if the render fn doesn't need it
303245 const keyRef = needKey ? shallowRef ( key ) : undefined
@@ -321,23 +263,14 @@ export const createFor = (
321263 itemRef ,
322264 keyRef ,
323265 indexRef ,
324- getKey && getKey ( item , key , index ) ,
266+ key2 ,
325267 ) )
326268
327269 if ( parent ) insert ( block . nodes , parent , anchor )
328270
329271 return block
330272 }
331273
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-
341274 const update = (
342275 { itemRef, keyRef, indexRef } : ForBlock ,
343276 newItem : any ,
0 commit comments