@@ -133,6 +133,12 @@ export const vnode_diff = (
133
133
/// and is not connected to the tree.
134
134
let vNewNode : VNode | null = null ;
135
135
136
+ /// When elements have keys they can be consumed out of order and therefore we can't use nextSibling.
137
+ /// In such a case this array will contain the elements after the current location.
138
+ /// The array even indices will contains keys and odd indices the vNode.
139
+ let vSiblings : Map < string , VNode > | null = null ;
140
+ let vSiblingsArray : Array < string | VNode | null > | null = null ;
141
+
136
142
/// Current set of JSX children.
137
143
let jsxChildren : JSXChildren [ ] = null ! ;
138
144
// Current JSX child.
@@ -315,6 +321,8 @@ export const vnode_diff = (
315
321
stackPush ( children , descendVNode ) ;
316
322
if ( descendVNode ) {
317
323
assertDefined ( vCurrent || vNewNode , 'Expecting vCurrent to be defined.' ) ;
324
+ vSiblings = null ;
325
+ vSiblingsArray = null ;
318
326
vParent = vNewNode || vCurrent ! ;
319
327
vCurrent = vnode_getFirstChild ( vParent ) ;
320
328
vNewNode = null ;
@@ -325,6 +333,8 @@ export const vnode_diff = (
325
333
function ascend ( ) {
326
334
const descendVNode = stack . pop ( ) ; // boolean: descendVNode
327
335
if ( descendVNode ) {
336
+ vSiblings = stack . pop ( ) ;
337
+ vSiblingsArray = stack . pop ( ) ;
328
338
vNewNode = stack . pop ( ) ;
329
339
vCurrent = stack . pop ( ) ;
330
340
vParent = stack . pop ( ) ;
@@ -339,7 +349,7 @@ export const vnode_diff = (
339
349
function stackPush ( children : JSXChildren , descendVNode : boolean ) {
340
350
stack . push ( jsxChildren , jsxIdx , jsxCount , jsxValue ) ;
341
351
if ( descendVNode ) {
342
- stack . push ( vParent , vCurrent , vNewNode ) ;
352
+ stack . push ( vParent , vCurrent , vNewNode , vSiblingsArray , vSiblings ) ;
343
353
}
344
354
stack . push ( descendVNode ) ;
345
355
if ( Array . isArray ( children ) ) {
@@ -820,7 +830,12 @@ export const vnode_diff = (
820
830
value = trackSignalAndAssignHost ( value , vnode , key , container , signalData ) ;
821
831
}
822
832
823
- vnode_setAttr ( journal , vnode , key , serializeAttribute ( key , value , scopedStyleIdPrefix ) ) ;
833
+ vnode_setAttr (
834
+ journal ,
835
+ vnode ,
836
+ key ,
837
+ value !== null ? serializeAttribute ( key , value , scopedStyleIdPrefix ) : null
838
+ ) ;
824
839
if ( value === null ) {
825
840
// if we set `null` than attribute was removed and we need to shorten the dstLength
826
841
dstLength = dstAttrs . length ;
@@ -926,21 +941,59 @@ export const vnode_diff = (
926
941
}
927
942
}
928
943
929
- /** Retrieve the child with the given key. */
944
+ /**
945
+ * This function is used to retrieve the child with the given key. If the child is not found, it
946
+ * will return null.
947
+ *
948
+ * After finding the first child with the given key we will create a map of all the keyed siblings
949
+ * and an array of non-keyed siblings. This is done to optimize the search for the next child with
950
+ * the specified key.
951
+ *
952
+ * @param nodeName - The name of the node.
953
+ * @param key - The key of the node.
954
+ * @returns The child with the given key or null if not found.
955
+ */
930
956
function retrieveChildWithKey (
931
957
nodeName : string | null ,
932
958
key : string | null
933
959
) : ElementVNode | VirtualVNode | null {
934
960
let vNodeWithKey : ElementVNode | VirtualVNode | null = null ;
935
- let vNode = vCurrent ;
936
- while ( vNode ) {
937
- const name = vnode_isElementVNode ( vNode ) ? vnode_getElementName ( vNode ) : null ;
938
- const vKey = getKey ( vNode ) || getComponentHash ( vNode , container . $getObjectById$ ) ;
939
- if ( vKey == key && name == nodeName ) {
940
- vNodeWithKey = vNode as ElementVNode | VirtualVNode ;
941
- break ;
961
+ if ( vSiblings === null ) {
962
+ // it is not materialized; so materialize it.
963
+ vSiblings = new Map < string , VNode > ( ) ;
964
+ vSiblingsArray = [ ] ;
965
+ let vNode = vCurrent ;
966
+ while ( vNode ) {
967
+ const name = vnode_isElementVNode ( vNode ) ? vnode_getElementName ( vNode ) : null ;
968
+ const vKey = getKey ( vNode ) || getComponentHash ( vNode , container . $getObjectById$ ) ;
969
+ if ( vNodeWithKey === null && vKey == key && name == nodeName ) {
970
+ vNodeWithKey = vNode as ElementVNode | VirtualVNode ;
971
+ } else {
972
+ if ( vKey === null ) {
973
+ vSiblingsArray . push ( name , vNode ) ;
974
+ } else {
975
+ // we only add the elements which we did not find yet.
976
+ vSiblings . set ( name + ':' + vKey , vNode ) ;
977
+ }
978
+ }
979
+ vNode = vnode_getNextSibling ( vNode ) ;
980
+ }
981
+ } else {
982
+ if ( key === null ) {
983
+ for ( let i = 0 ; i < vSiblingsArray ! . length ; i += 2 ) {
984
+ if ( vSiblingsArray ! [ i ] === nodeName ) {
985
+ vNodeWithKey = vSiblingsArray ! [ i + 1 ] as ElementVNode | VirtualVNode ;
986
+ vSiblingsArray ! . splice ( i , 2 ) ;
987
+ break ;
988
+ }
989
+ }
990
+ } else {
991
+ const vSibling = vSiblings . get ( nodeName + ':' + key ) ;
992
+ if ( vSibling ) {
993
+ vNodeWithKey = vSibling as ElementVNode | VirtualVNode ;
994
+ vSiblings . delete ( nodeName + ':' + key ) ;
995
+ }
942
996
}
943
- vNode = vnode_getNextSibling ( vNode ) ;
944
997
}
945
998
return vNodeWithKey ;
946
999
}
0 commit comments