@@ -34,6 +34,9 @@ import {
3434class ForBlock extends VaporFragment {
3535 scope : EffectScope | undefined
3636 key : any
37+ prev ?: ForBlock
38+ next ?: ForBlock
39+ prevAnchor ?: ForBlock
3740
3841 itemRef : ShallowRef < any >
3942 keyRef : ShallowRef < any > | undefined
@@ -90,7 +93,7 @@ export const createFor = (
9093 let oldBlocks : ForBlock [ ] = [ ]
9194 let newBlocks : ForBlock [ ]
9295 let parent : ParentNode | undefined | null
93- // useSelector only
96+ // createSelector only
9497 let currentKey : any
9598 // TODO handle this in hydration
9699 const parentAnchor = __DEV__ ? createComment ( 'for' ) : createTextNode ( )
@@ -171,169 +174,169 @@ export const createFor = (
171174 }
172175 }
173176
174- const sharedBlockCount = Math . min ( oldLength , newLength )
175- const previousKeyIndexPairs : [ any , number ] [ ] = new Array ( oldLength )
177+ const commonLength = Math . min ( oldLength , newLength )
178+ const oldKeyIndexPairs : [ any , number ] [ ] = new Array ( oldLength )
176179 const queuedBlocks : [
177- blockIndex : number ,
178- blockItem : ReturnType < typeof getItem > ,
179- blockKey : any ,
180+ index : number ,
181+ item : ReturnType < typeof getItem > ,
182+ key : any ,
180183 ] [ ] = new Array ( newLength )
181184
182- let anchorFallback : Node = parentAnchor
183185 let endOffset = 0
184- let startOffset = 0
185- let queuedBlocksInsertIndex = 0
186- let previousKeyIndexInsertIndex = 0
186+ let queuedBlocksLength = 0
187+ let oldKeyIndexPairsLength = 0
187188
188- while ( endOffset < sharedBlockCount ) {
189- const currentIndex = newLength - endOffset - 1
190- const currentItem = getItem ( source , currentIndex )
191- const currentKey = getKey ( ...currentItem )
189+ while ( endOffset < commonLength ) {
190+ const index = newLength - endOffset - 1
191+ const item = getItem ( source , index )
192+ const key = getKey ( ...item )
192193 const existingBlock = oldBlocks [ oldLength - endOffset - 1 ]
193- if ( existingBlock . key === currentKey ) {
194- update ( existingBlock , ...currentItem )
195- newBlocks [ currentIndex ] = existingBlock
196- endOffset ++
197- continue
198- }
199- break
194+ if ( existingBlock . key !== key ) break
195+ update ( existingBlock , ...item )
196+ newBlocks [ index ] = existingBlock
197+ endOffset ++
200198 }
201199
202- if ( endOffset !== 0 ) {
203- anchorFallback = normalizeAnchor (
204- newBlocks [ newLength - endOffset ] . nodes ,
205- )
206- }
200+ const e1 = commonLength - endOffset
201+ const e2 = oldLength - endOffset
202+ const e3 = newLength - endOffset
207203
208- while ( startOffset < sharedBlockCount - endOffset ) {
209- const currentItem = getItem ( source , startOffset )
204+ for ( let i = 0 ; i < e1 ; i ++ ) {
205+ const currentItem = getItem ( source , i )
210206 const currentKey = getKey ( ...currentItem )
211- const previousBlock = oldBlocks [ startOffset ]
212- const previousKey = previousBlock . key
213- if ( previousKey === currentKey ) {
214- update ( ( newBlocks [ startOffset ] = previousBlock ) , currentItem [ 0 ] )
207+ const oldBlock = oldBlocks [ i ]
208+ const oldKey = oldBlock . key
209+ if ( oldKey === currentKey ) {
210+ update ( ( newBlocks [ i ] = oldBlock ) , currentItem [ 0 ] )
215211 } else {
216- queuedBlocks [ queuedBlocksInsertIndex ++ ] = [
217- startOffset ,
218- currentItem ,
219- currentKey ,
220- ]
221- previousKeyIndexPairs [ previousKeyIndexInsertIndex ++ ] = [
222- previousKey ,
223- startOffset ,
224- ]
212+ queuedBlocks [ queuedBlocksLength ++ ] = [ i , currentItem , currentKey ]
213+ oldKeyIndexPairs [ oldKeyIndexPairsLength ++ ] = [ oldKey , i ]
225214 }
226- startOffset ++
227215 }
228216
229- for ( let i = startOffset ; i < oldLength - endOffset ; i ++ ) {
230- previousKeyIndexPairs [ previousKeyIndexInsertIndex ++ ] = [
231- oldBlocks [ i ] . key ,
232- i ,
233- ]
217+ for ( let i = e1 ; i < e2 ; i ++ ) {
218+ oldKeyIndexPairs [ oldKeyIndexPairsLength ++ ] = [ oldBlocks [ i ] . key , i ]
234219 }
235220
236- const preparationBlockCount = Math . min (
237- newLength - endOffset ,
238- sharedBlockCount ,
239- )
240- for ( let i = startOffset ; i < preparationBlockCount ; i ++ ) {
221+ for ( let i = e1 ; i < e3 ; i ++ ) {
241222 const blockItem = getItem ( source , i )
242223 const blockKey = getKey ( ...blockItem )
243- queuedBlocks [ queuedBlocksInsertIndex ++ ] = [ i , blockItem , blockKey ]
224+ queuedBlocks [ queuedBlocksLength ++ ] = [ i , blockItem , blockKey ]
244225 }
245226
246- if ( ! queuedBlocksInsertIndex && ! previousKeyIndexInsertIndex ) {
247- for ( let i = preparationBlockCount ; i < newLength - endOffset ; i ++ ) {
248- const blockItem = getItem ( source , i )
249- const blockKey = getKey ( ...blockItem )
250- mount ( source , i , anchorFallback , blockItem , blockKey )
251- }
252- } else {
253- queuedBlocks . length = queuedBlocksInsertIndex
254- previousKeyIndexPairs . length = previousKeyIndexInsertIndex
255-
256- const previousKeyIndexMap = new Map ( previousKeyIndexPairs )
257- const operations : ( ( ) => void ) [ ] = [ ]
258-
259- let mountCounter = 0
260- const relocateOrMountBlock = (
261- blockIndex : number ,
262- blockItem : ReturnType < typeof getItem > ,
263- blockKey : any ,
264- anchorOffset : number ,
265- ) => {
266- const previousIndex = previousKeyIndexMap . get ( blockKey )
267- if ( previousIndex !== undefined ) {
268- const reusedBlock = ( newBlocks [ blockIndex ] =
269- oldBlocks [ previousIndex ] )
270- update ( reusedBlock , ...blockItem )
271- previousKeyIndexMap . delete ( blockKey )
272- if ( previousIndex !== blockIndex ) {
273- operations . push ( ( ) =>
274- insert (
275- reusedBlock ,
276- parent ! ,
277- anchorOffset === - 1
278- ? anchorFallback
279- : normalizeAnchor ( newBlocks [ anchorOffset ] . nodes ) ,
280- ) ,
281- )
282- }
283- } else {
284- mountCounter ++
285- operations . push ( ( ) =>
286- mount (
287- source ,
288- blockIndex ,
289- anchorOffset === - 1
290- ? anchorFallback
291- : normalizeAnchor ( newBlocks [ anchorOffset ] . nodes ) ,
292- blockItem ,
293- blockKey ,
294- ) ,
295- )
296- }
297- }
227+ queuedBlocks . length = queuedBlocksLength
228+ oldKeyIndexPairs . length = oldKeyIndexPairsLength
298229
299- for ( let i = queuedBlocks . length - 1 ; i >= 0 ; i -- ) {
300- const [ blockIndex , blockItem , blockKey ] = queuedBlocks [ i ]
301- relocateOrMountBlock (
302- blockIndex ,
303- blockItem ,
304- blockKey ,
305- blockIndex < preparationBlockCount - 1 ? blockIndex + 1 : - 1 ,
306- )
307- }
230+ interface MountOper {
231+ source : ResolvedSource
232+ index : number
233+ item : ReturnType < typeof getItem >
234+ key : any
235+ }
236+ interface MoveOper {
237+ index : number
238+ block : ForBlock
239+ }
240+
241+ const oldKeyIndexMap = new Map ( oldKeyIndexPairs )
242+ const opers : ( MountOper | MoveOper ) [ ] = new Array ( queuedBlocks . length )
308243
309- for ( let i = preparationBlockCount ; i < newLength - endOffset ; i ++ ) {
310- const blockItem = getItem ( source , i )
311- const blockKey = getKey ( ...blockItem )
312- relocateOrMountBlock ( i , blockItem , blockKey , - 1 )
244+ let mountCounter = 0
245+ let opersLength = 0
246+
247+ for ( let i = queuedBlocks . length - 1 ; i >= 0 ; i -- ) {
248+ const [ index , item , key ] = queuedBlocks [ i ]
249+ const oldIndex = oldKeyIndexMap . get ( key )
250+ if ( oldIndex !== undefined ) {
251+ oldKeyIndexMap . delete ( key )
252+ const reusedBlock = ( newBlocks [ index ] = oldBlocks [ oldIndex ] )
253+ update ( reusedBlock , ...item )
254+ opers [ opersLength ++ ] = { index, block : reusedBlock }
255+ } else {
256+ mountCounter ++
257+ opers [ opersLength ++ ] = { source, index, item, key }
313258 }
259+ }
260+
261+ const useFastRemove = mountCounter === newLength
314262
315- const useFastRemove = mountCounter === newLength
263+ for ( const leftoverIndex of oldKeyIndexMap . values ( ) ) {
264+ unmount (
265+ oldBlocks [ leftoverIndex ] ,
266+ ! ( useFastRemove && canUseFastRemove ) ,
267+ ! useFastRemove ,
268+ )
269+ }
270+ if ( useFastRemove ) {
271+ for ( const selector of selectors ) {
272+ selector . cleanup ( )
273+ }
274+ if ( canUseFastRemove ) {
275+ parent ! . textContent = ''
276+ parent ! . appendChild ( parentAnchor )
277+ }
278+ }
316279
317- for ( const leftoverIndex of previousKeyIndexMap . values ( ) ) {
318- unmount (
319- oldBlocks [ leftoverIndex ] ,
320- ! ( useFastRemove && canUseFastRemove ) ,
321- ! useFastRemove ,
280+ if ( opers . length === mountCounter ) {
281+ for ( const { source, index, item, key } of opers as MountOper [ ] ) {
282+ mount (
283+ source ,
284+ index ,
285+ index < newLength - 1
286+ ? normalizeAnchor ( newBlocks [ index + 1 ] . nodes )
287+ : parentAnchor ,
288+ item ,
289+ key ,
322290 )
323291 }
324- if ( useFastRemove ) {
325- for ( const selector of selectors ) {
326- selector . cleanup ( )
292+ } else if ( opers . length ) {
293+ let anchor = oldBlocks [ 0 ]
294+ let blocksTail : ForBlock | undefined
295+ for ( let i = 0 ; i < oldLength ; i ++ ) {
296+ const block = oldBlocks [ i ]
297+ if ( oldKeyIndexMap . has ( block . key ) ) {
298+ continue
327299 }
328- if ( canUseFastRemove ) {
329- parent ! . textContent = ''
330- parent ! . appendChild ( parentAnchor )
300+ block . prevAnchor = anchor
301+ anchor = oldBlocks [ i + 1 ]
302+ if ( blocksTail !== undefined ) {
303+ blocksTail . next = block
304+ block . prev = blocksTail
331305 }
306+ blocksTail = block
332307 }
333-
334- // perform mount and move operations
335- for ( const action of operations ) {
336- action ( )
308+ for ( const action of opers ) {
309+ const { index } = action
310+ if ( index < newLength - 1 ) {
311+ const nextBlock = newBlocks [ index + 1 ]
312+ let anchorNode = normalizeAnchor ( nextBlock . prevAnchor ! . nodes )
313+ if ( ! anchorNode . parentNode )
314+ anchorNode = normalizeAnchor ( nextBlock . nodes )
315+ if ( 'source' in action ) {
316+ const { item, key } = action
317+ const block = mount ( source , index , anchorNode , item , key )
318+ moveLink ( block , nextBlock . prev , nextBlock )
319+ } else if ( action . block . next !== nextBlock ) {
320+ insert ( action . block , parent ! , anchorNode )
321+ moveLink ( action . block , nextBlock . prev , nextBlock )
322+ }
323+ } else if ( 'source' in action ) {
324+ const { item, key } = action
325+ const block = mount ( source , index , parentAnchor , item , key )
326+ moveLink ( block , blocksTail )
327+ blocksTail = block
328+ } else if ( action . block . next !== undefined ) {
329+ let anchorNode = anchor
330+ ? normalizeAnchor ( anchor . nodes )
331+ : parentAnchor
332+ if ( ! anchorNode . parentNode ) anchorNode = parentAnchor
333+ insert ( action . block , parent ! , anchorNode )
334+ moveLink ( action . block , blocksTail )
335+ blocksTail = action . block
336+ }
337+ }
338+ for ( const block of newBlocks ) {
339+ block . prevAnchor = block . next = block . prev = undefined
337340 }
338341 }
339342 }
@@ -486,6 +489,22 @@ export const createFor = (
486489 }
487490}
488491
492+ function moveLink ( block : ForBlock , newPrev ?: ForBlock , newNext ?: ForBlock ) {
493+ const { prev : oldPrev , next : oldNext } = block
494+ if ( oldPrev ) oldPrev . next = oldNext
495+ if ( oldNext ) {
496+ oldNext . prev = oldPrev
497+ if ( block . prevAnchor !== block ) {
498+ oldNext . prevAnchor = block . prevAnchor
499+ }
500+ }
501+ if ( newPrev ) newPrev . next = block
502+ if ( newNext ) newNext . prev = block
503+ block . prev = newPrev
504+ block . next = newNext
505+ block . prevAnchor = block
506+ }
507+
489508export function createForSlots (
490509 rawSource : Source ,
491510 getSlot : ( item : any , key : any , index ?: number ) => DynamicSlot ,
0 commit comments