@@ -123,6 +123,7 @@ export const vnode_diff = (
123
123
/// and is not connected to the tree.
124
124
let vNewNode : VNode | null = null ;
125
125
126
+ let vSiblings : Map < string , VNode > | null = null ;
126
127
/// The array even indices will contains keys and odd indices the non keyed siblings.
127
128
let vSiblingsArray : Array < string | VNode | null > | null = null ;
128
129
@@ -319,6 +320,7 @@ export const vnode_diff = (
319
320
if ( descendVNode ) {
320
321
assertDefined ( vCurrent || vNewNode , 'Expecting vCurrent to be defined.' ) ;
321
322
vSideBuffer = null ;
323
+ vSiblings = null ;
322
324
vSiblingsArray = null ;
323
325
vParent = ( vNewNode || vCurrent ! ) as ElementVNode | VirtualVNode ;
324
326
vCurrent = vnode_getFirstChild ( vParent ) ;
@@ -331,6 +333,7 @@ export const vnode_diff = (
331
333
const descendVNode = stack . pop ( ) ; // boolean: descendVNode
332
334
if ( descendVNode ) {
333
335
vSideBuffer = stack . pop ( ) ;
336
+ vSiblings = stack . pop ( ) ;
334
337
vSiblingsArray = stack . pop ( ) ;
335
338
vNewNode = stack . pop ( ) ;
336
339
vCurrent = stack . pop ( ) ;
@@ -346,7 +349,7 @@ export const vnode_diff = (
346
349
function stackPush ( children : JSXChildren , descendVNode : boolean ) {
347
350
stack . push ( jsxChildren , jsxIdx , jsxCount , jsxValue ) ;
348
351
if ( descendVNode ) {
349
- stack . push ( vParent , vCurrent , vNewNode , vSiblingsArray , vSideBuffer ) ;
352
+ stack . push ( vParent , vCurrent , vNewNode , vSiblingsArray , vSiblings , vSideBuffer ) ;
350
353
}
351
354
stack . push ( descendVNode ) ;
352
355
if ( Array . isArray ( children ) ) {
@@ -937,94 +940,85 @@ export const vnode_diff = (
937
940
}
938
941
}
939
942
940
- /**
941
- * This function is used to retrieve the child with the given key. If the child is not found, it
942
- * will return null.
943
- *
944
- * We will also collect all the keyed siblings found before the target key and add them to the
945
- * side buffer. This is done to optimize the search for the next child with the specified key.
946
- *
947
- * @param nodeName - The name of the node.
948
- * @param key - The key of the node.
949
- * @returns The child with the given key or null if not found.
950
- */
951
943
function retrieveChildWithKey (
952
944
nodeName : string | null ,
953
945
key : string | null
954
946
) : ElementVNode | VirtualVNode | null {
955
947
let vNodeWithKey : ElementVNode | VirtualVNode | null = null ;
956
-
957
- // if key is null we need to:
958
- // - if this is the first time fill the vSiblingsArray with all siblings
959
- // - if not then find the node we are interested in
960
-
961
- if ( key == null && vSiblingsArray != null ) {
962
- for ( let i = 0 ; i < vSiblingsArray . length ; i += 2 ) {
963
- if ( vSiblingsArray [ i ] === nodeName ) {
964
- vNodeWithKey = vSiblingsArray ! [ i + 1 ] as ElementVNode | VirtualVNode ;
965
- vSiblingsArray . splice ( i , 2 ) ;
966
- break ;
948
+ if ( vSiblings === null ) {
949
+ // it is not materialized; so materialize it.
950
+ vSiblings = new Map < string , VNode > ( ) ;
951
+ vSiblingsArray = [ ] ;
952
+ let vNode = vCurrent ;
953
+ while ( vNode ) {
954
+ const name = vnode_isElementVNode ( vNode ) ? vnode_getElementName ( vNode ) : null ;
955
+ const vKey = getKey ( vNode ) || getComponentHash ( vNode , container . $getObjectById$ ) ;
956
+ if ( vNodeWithKey === null && vKey == key && name == nodeName ) {
957
+ vNodeWithKey = vNode as ElementVNode | VirtualVNode ;
958
+ } else {
959
+ if ( vKey === null ) {
960
+ vSiblingsArray . push ( name , vNode ) ;
961
+ } else {
962
+ // we only add the elements which we did not find yet.
963
+ vSiblings . set ( getSideBufferKey ( name , vKey ) , vNode ) ;
964
+ }
965
+ }
966
+ vNode = vNode . nextSibling as VNode | null ;
967
+ }
968
+ } else {
969
+ if ( key === null ) {
970
+ for ( let i = 0 ; i < vSiblingsArray ! . length ; i += 2 ) {
971
+ if ( vSiblingsArray ! [ i ] === nodeName ) {
972
+ vNodeWithKey = vSiblingsArray ! [ i + 1 ] as ElementVNode | VirtualVNode ;
973
+ vSiblingsArray ! . splice ( i , 2 ) ;
974
+ break ;
975
+ }
976
+ }
977
+ } else {
978
+ const siblingsKey = getSideBufferKey ( nodeName , key ) ;
979
+ if ( vSiblings . has ( siblingsKey ) ) {
980
+ vNodeWithKey = vSiblings . get ( siblingsKey ) as ElementVNode | VirtualVNode ;
981
+ vSiblings . delete ( siblingsKey ) ;
967
982
}
968
983
}
969
- return vNodeWithKey ;
970
984
}
971
985
972
- const fillSiblingsArray = vSiblingsArray == null ;
973
- let vNode = vCurrent ;
974
- let foundTarget = false ;
975
- let keyedSiblingsBeforeTarget : Array < {
976
- sideBufferKey : string ;
977
- vNode : VNode ;
978
- } > | null = null ;
986
+ collectSideBufferSiblings ( vNodeWithKey ) ;
979
987
980
- while ( vNode ) {
981
- const name = vnode_isElementVNode ( vNode ) ? vnode_getElementName ( vNode ) : null ;
982
- const vKey = getKey ( vNode ) || getComponentHash ( vNode , container . $getObjectById$ ) ;
988
+ return vNodeWithKey ;
989
+ }
983
990
984
- if ( vNodeWithKey === null && vKey == key && name == nodeName ) {
985
- vNodeWithKey = vNode as ElementVNode | VirtualVNode ;
986
- foundTarget = true ;
987
- if ( keyedSiblingsBeforeTarget && keyedSiblingsBeforeTarget . length > 0 ) {
991
+ function collectSideBufferSiblings ( targetNode : VNode | null ) : void {
992
+ if ( ! targetNode ) {
993
+ if ( vCurrent ) {
994
+ const name = vnode_isElementVNode ( vCurrent ) ? vnode_getElementName ( vCurrent ) : null ;
995
+ const vKey = getKey ( vCurrent ) || getComponentHash ( vCurrent , container . $getObjectById$ ) ;
996
+ if ( vKey != null ) {
997
+ const sideBufferKey = getSideBufferKey ( name , vKey ) ;
988
998
vSideBuffer ||= new Map ( ) ;
989
- // Add all collected keyed siblings to side buffer now that we found the target
990
- for ( const sibling of keyedSiblingsBeforeTarget ) {
991
- vSideBuffer . set ( sibling . sideBufferKey , sibling . vNode ) ;
992
- }
993
- }
994
- if ( ! fillSiblingsArray ) {
995
- break ;
996
- }
997
- } else {
998
- if ( vKey == null ) {
999
- if ( fillSiblingsArray ) {
1000
- // Unkeyed sibling - add to siblings array
1001
- vSiblingsArray ||= [ ] ;
1002
- vSiblingsArray . push ( name , vNode ) ;
1003
- }
1004
- } else {
1005
- if ( ! foundTarget ) {
1006
- keyedSiblingsBeforeTarget ||= [ ] ;
1007
- const sideBufferKey = getSideBufferKey ( name , vKey ) ;
1008
- // Collect keyed sibling found before target
1009
- keyedSiblingsBeforeTarget . push ( { sideBufferKey, vNode } ) ;
1010
- }
999
+ vSideBuffer . set ( sideBufferKey , vCurrent ) ;
1000
+ vSiblings ?. delete ( sideBufferKey ) ;
1011
1001
}
1012
1002
}
1013
1003
1014
- vNode = vNode . nextSibling as VNode | null ;
1004
+ return ;
1015
1005
}
1016
1006
1017
- // add current to the side buffer if it is not the target
1018
- if ( ! foundTarget && vCurrent ) {
1019
- const name = vnode_isElementVNode ( vCurrent ) ? vnode_getElementName ( vCurrent ) : null ;
1020
- const vKey = getKey ( vCurrent ) || getComponentHash ( vCurrent , container . $getObjectById$ ) ;
1007
+ // Walk from vCurrent up to the target node and collect all keyed siblings
1008
+ let vNode = vCurrent ;
1009
+ while ( vNode && vNode !== targetNode ) {
1010
+ const name = vnode_isElementVNode ( vNode ) ? vnode_getElementName ( vNode ) : null ;
1011
+ const vKey = getKey ( vNode ) || getComponentHash ( vNode , container . $getObjectById$ ) ;
1012
+
1021
1013
if ( vKey != null ) {
1022
1014
const sideBufferKey = getSideBufferKey ( name , vKey ) ;
1023
1015
vSideBuffer ||= new Map ( ) ;
1024
- vSideBuffer . set ( sideBufferKey , vCurrent ) ;
1016
+ vSideBuffer . set ( sideBufferKey , vNode ) ;
1017
+ vSiblings ?. delete ( sideBufferKey ) ;
1025
1018
}
1019
+
1020
+ vNode = vNode . nextSibling as VNode | null ;
1026
1021
}
1027
- return vNodeWithKey ;
1028
1022
}
1029
1023
1030
1024
function getSideBufferKey ( nodeName : string | null , key : string ) : string ;
@@ -1066,6 +1060,7 @@ export const vnode_diff = (
1066
1060
) : any {
1067
1061
// 1) Try to find the node among upcoming siblings
1068
1062
vNewNode = retrieveChildWithKey ( nodeName , lookupKey ) ;
1063
+
1069
1064
if ( vNewNode ) {
1070
1065
vCurrent = vNewNode ;
1071
1066
vNewNode = null ;
0 commit comments