@wsxjs/wsx-core 0.0.22 → 0.0.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,10 +6,25 @@
6
6
  */
7
7
 
8
8
  import { shouldUseSVGNamespace, getSVGAttributeName } from "./svg-utils";
9
- import { flattenChildren, type JSXChildren } from "./dom-utils";
9
+ import { type JSXChildren } from "./dom-utils";
10
10
  import { setSmartProperty, isFrameworkInternalProp } from "./props-utils";
11
- import { shouldPreserveElement, getElementCacheKey } from "./element-marking";
11
+ import { shouldPreserveElement } from "./element-marking";
12
12
  import type { DOMCacheManager } from "../dom-cache-manager";
13
+ import {
14
+ collectPreservedElements,
15
+ findElementNode,
16
+ findTextNode,
17
+ updateOrCreateTextNode,
18
+ removeNodeIfNotPreserved,
19
+ replaceOrInsertElement,
20
+ appendNewChild,
21
+ buildNewChildrenMaps,
22
+ deduplicateCacheKeys,
23
+ collectNodesToRemove,
24
+ removeNodes,
25
+ reinsertPreservedElements,
26
+ flattenChildrenSafe,
27
+ } from "./update-children-helpers";
13
28
 
14
29
  /**
15
30
  * Removes a property from an element.
@@ -236,205 +251,129 @@ export function updateProps(
236
251
  export function updateChildren(
237
252
  element: HTMLElement | SVGElement,
238
253
  oldChildren: JSXChildren[],
239
- newChildren: JSXChildren[]
254
+ newChildren: JSXChildren[],
255
+ cacheManager?: DOMCacheManager
240
256
  ): void {
241
- const flatOld = flattenChildren(oldChildren);
242
- const flatNew = flattenChildren(newChildren);
257
+ const flatOld = flattenChildrenSafe(oldChildren);
258
+ const flatNew = flattenChildrenSafe(newChildren);
243
259
 
244
- // 阶段 4 简化版:只处理相同数量的子节点
245
- const minLength = Math.min(flatOld.length, flatNew.length);
260
+ // 收集需要保留的元素(第三方库注入的元素)
261
+ const preservedElements = collectPreservedElements(element);
246
262
 
247
263
  // 更新现有子节点
248
- // 关键:直接使用 oldChild 作为 oldNode(如果它是元素),因为它已经在 DOM 中
249
- // 对于文本节点,按顺序匹配(跳过应该保留的元素节点)
250
- let domIndex = 0; // DOM 中的实际索引,用于匹配文本节点
264
+ const minLength = Math.min(flatOld.length, flatNew.length);
265
+ const domIndex = { value: 0 }; // 使用对象包装,使其可在函数间传递
266
+
251
267
  for (let i = 0; i < minLength; i++) {
252
268
  const oldChild = flatOld[i];
253
269
  const newChild = flatNew[i];
254
270
 
255
- // 找到与 oldChild 对应的实际 DOM 节点
256
- // 关键:oldChild 是上次渲染的元素引用,如果它在 DOM 中,直接使用它
271
+ // 查找对应的 DOM 节点
257
272
  let oldNode: Node | null = null;
258
273
  if (oldChild instanceof HTMLElement || oldChild instanceof SVGElement) {
259
- // 元素节点:检查 oldChild 是否在 DOM 中
260
- // 如果 oldChild 的 parentNode 是当前 element,说明它在 DOM 中
261
- if (oldChild.parentNode === element) {
262
- // 关键修复:确保 oldChild 不是应该保留的元素(手动创建的元素、第三方库注入的元素)
263
- // 如果 oldChild 是应该保留的元素,不应该在"更新现有子节点"循环中处理它
264
- if (!shouldPreserveElement(oldChild)) {
265
- oldNode = oldChild;
266
- }
267
- } else {
268
- // oldChild 不在 DOM 中,尝试通过 cache key 找到对应的 DOM 节点
269
- // 这可以处理元素被替换但 cache key 相同的情况
270
- const oldCacheKey = getElementCacheKey(oldChild);
271
- if (oldCacheKey) {
272
- // 遍历 DOM 中的子节点,找到具有相同 cache key 的节点
273
- for (let j = 0; j < element.childNodes.length; j++) {
274
- const domChild = element.childNodes[j];
275
- if (domChild instanceof HTMLElement || domChild instanceof SVGElement) {
276
- // 跳过应该保留的元素(手动创建的元素、第三方库注入的元素)
277
- if (shouldPreserveElement(domChild)) {
278
- continue;
279
- }
280
- const domCacheKey = getElementCacheKey(domChild);
281
- if (domCacheKey === oldCacheKey) {
282
- oldNode = domChild;
283
- break;
284
- }
285
- }
286
- }
274
+ oldNode = findElementNode(oldChild, element);
275
+ // 关键修复:当处理元素节点时,需要更新 domIndex 以跳过该元素
276
+ // 这样,下一个文本节点的查找位置才是正确的
277
+ if (oldNode && oldNode.parentNode === element) {
278
+ // 找到 oldNode 在 DOM 中的位置
279
+ const nodeIndex = Array.from(element.childNodes).indexOf(oldNode as ChildNode);
280
+ if (nodeIndex !== -1 && nodeIndex >= domIndex.value) {
281
+ // 更新 domIndex 到 oldNode 之后的位置
282
+ domIndex.value = nodeIndex + 1;
287
283
  }
288
284
  }
289
- // 如果 oldChild 不在 DOM 中且找不到对应的节点,oldNode 保持为 null
290
285
  } else if (typeof oldChild === "string" || typeof oldChild === "number") {
291
- // 文本节点:按顺序找到对应的文本节点(跳过所有元素节点)
292
- // 关键:跳过应该保留的元素节点(手动创建的元素、第三方库注入的元素)
293
- while (domIndex < element.childNodes.length) {
294
- const node = element.childNodes[domIndex];
295
- if (node.nodeType === Node.TEXT_NODE) {
296
- oldNode = node;
297
- domIndex++;
298
- break;
299
- } else if (node.nodeType === Node.ELEMENT_NODE) {
300
- // 跳过所有元素节点(不管是保留的还是框架管理的)
301
- // 因为元素节点会在自己的迭代中处理
302
- // 注意:手动创建的元素会被 shouldPreserveElement 保护,不会被移除
303
- domIndex++;
304
- } else {
305
- // 跳过其他类型的节点
306
- domIndex++;
286
+ oldNode = findTextNode(element, domIndex);
287
+ // 关键修复:如果 findTextNode 返回 null,尝试从当前 domIndex 位置开始查找文本节点
288
+ // 这可以处理文本节点存在但 domIndex 不正确的情况
289
+ // Bug 1 修复:从 domIndex.value 开始搜索,而不是从 0 开始,避免重新处理已处理的节点
290
+ if (!oldNode && element.childNodes.length > 0) {
291
+ for (let j = domIndex.value; j < element.childNodes.length; j++) {
292
+ const node = element.childNodes[j];
293
+ if (node.nodeType === Node.TEXT_NODE) {
294
+ oldNode = node;
295
+ // 更新 domIndex 到找到的文本节点之后
296
+ domIndex.value = j + 1;
297
+ break;
298
+ }
307
299
  }
308
300
  }
309
301
  }
310
302
 
311
- // 如果是文本节点,更新文本内容
303
+ // 处理文本节点(oldChild 是字符串/数字)
312
304
  if (typeof oldChild === "string" || typeof oldChild === "number") {
313
305
  if (typeof newChild === "string" || typeof newChild === "number") {
314
306
  const oldText = String(oldChild);
315
307
  const newText = String(newChild);
316
308
 
317
- // 关键修复:始终检查 DOM 中的实际文本内容,而不仅仅依赖 oldChild
318
- // 这样可以确保即使元数据不同步,也能正确更新
309
+ // Bug 2 修复:只有当文本内容确实需要更新时才调用 updateOrCreateTextNode
310
+ // 如果 oldText === newText 且 oldNode 为 null,说明文本节点可能已经存在且内容正确
311
+ // 或者不需要创建,因此不应该调用 updateOrCreateTextNode
319
312
  const needsUpdate =
320
313
  oldText !== newText ||
321
314
  (oldNode &&
322
315
  oldNode.nodeType === Node.TEXT_NODE &&
323
316
  oldNode.textContent !== newText);
324
317
 
325
- if (!needsUpdate) {
326
- // 文本内容确实相同,跳过更新
327
- continue;
328
- }
329
-
330
- if (oldNode && oldNode.nodeType === Node.TEXT_NODE) {
331
- // 更新现有文本节点
332
- oldNode.textContent = newText;
333
- } else {
334
- // 创建新的文本节点
335
- const newTextNode = document.createTextNode(newText);
336
- if (oldNode && !shouldPreserveElement(oldNode)) {
337
- element.replaceChild(newTextNode, oldNode);
338
- } else {
339
- element.insertBefore(newTextNode, oldNode || null);
340
- }
318
+ if (needsUpdate) {
319
+ updateOrCreateTextNode(element, oldNode, newText);
341
320
  }
321
+ // 如果文本内容相同且 oldNode 为 null,不需要做任何操作
322
+ // 因为文本节点可能已经存在于 DOM 中且内容正确,或者不需要创建
342
323
  } else {
343
- // 类型变化:文本 -> 元素
344
- if (oldNode && !shouldPreserveElement(oldNode)) {
345
- element.removeChild(oldNode);
346
- }
324
+ // 类型变化:文本 -> 元素/Fragment
325
+ removeNodeIfNotPreserved(element, oldNode);
347
326
  if (newChild instanceof HTMLElement || newChild instanceof SVGElement) {
348
- if (newChild.parentNode !== element) {
349
- element.insertBefore(newChild, oldNode || null);
350
- }
327
+ replaceOrInsertElement(element, newChild, oldNode);
351
328
  } else if (newChild instanceof DocumentFragment) {
352
329
  element.insertBefore(newChild, oldNode || null);
353
330
  }
354
331
  }
355
- } else if (oldChild instanceof HTMLElement || oldChild instanceof SVGElement) {
356
- // 关键修复:如果 oldNode 是应该保留的元素(手动创建的元素、第三方库注入的元素),跳过处理
357
- // 这些元素不在 oldChildren newChildren 中,应该在第二步被保留
332
+ }
333
+ // 处理元素节点(oldChild 是元素)
334
+ else if (oldChild instanceof HTMLElement || oldChild instanceof SVGElement) {
358
335
  if (oldNode && shouldPreserveElement(oldNode)) {
359
- // 跳过应该保留的元素,继续处理下一个
360
- continue;
336
+ continue; // 跳过保留的元素
361
337
  }
362
338
 
363
- // 如果是元素节点,检查是否是同一个元素
364
- if (newChild === oldChild) {
365
- // 同一个元素,不需要更新(元素内容会在 updateElement 中更新)
366
- continue;
367
- } else if (newChild instanceof HTMLElement || newChild instanceof SVGElement) {
368
- // 不同的元素引用,需要替换
369
- // 检查 cache key:如果 cache key 相同,说明是同一个逻辑位置,应该替换
370
- const oldCacheKey =
371
- oldNode && (oldNode instanceof HTMLElement || oldNode instanceof SVGElement)
372
- ? getElementCacheKey(oldNode)
373
- : null;
374
- const newCacheKey = getElementCacheKey(newChild);
375
- const hasSameCacheKey = oldCacheKey && newCacheKey && oldCacheKey === newCacheKey;
376
-
377
- if (oldNode) {
378
- // oldNode 存在(oldChild 在 DOM 中)
379
- if (!shouldPreserveElement(oldNode)) {
380
- // 可以替换
381
- if (oldNode !== newChild) {
382
- // 如果 newChild 已经在 DOM 中,需要先移除它(避免重复)
383
- if (newChild.parentNode === element) {
384
- // newChild 已经在当前 element 中
385
- // 如果 cache key 相同,说明是同一个逻辑位置,应该替换 oldNode
386
- if (hasSameCacheKey) {
387
- // 如果 newChild 就是 oldNode,不需要替换
388
- if (newChild !== oldNode) {
389
- // 替换旧元素(replaceChild 会自动从 newChild 的旧位置移除它)
390
- // 但是,如果 newChild 在 oldNode 之后,replaceChild 会先移除 newChild,然后替换 oldNode
391
- // 这会导致 newChild 移动到 oldNode 的位置,这是正确的
392
- // 如果 newChild 在 oldNode 之前,replaceChild 也会将 newChild 移动到 oldNode 的位置
393
- // 所以,无论 newChild 在哪里,replaceChild 都会将其移动到 oldNode 的位置
394
- element.replaceChild(newChild, oldNode);
395
- }
396
- } else {
397
- // cache key 不同,说明 newChild 在错误的位置
398
- // 先移除 newChild(它可能在错误的位置)
399
- element.removeChild(newChild);
400
- // 然后替换 oldNode
401
- element.replaceChild(newChild, oldNode);
402
- }
403
- } else if (newChild.parentNode) {
404
- // newChild 在其他父元素中,先移除
405
- newChild.parentNode.removeChild(newChild);
406
- // 然后替换 oldNode
407
- element.replaceChild(newChild, oldNode);
408
- } else {
409
- // newChild 不在 DOM 中,直接替换
410
- element.replaceChild(newChild, oldNode);
411
- }
412
- }
413
- } else {
414
- // 应该保留旧节点,只添加新节点(不替换)
415
- if (newChild.parentNode !== element) {
416
- // 如果 newChild 在其他父元素中,先移除
417
- if (newChild.parentNode) {
418
- newChild.parentNode.removeChild(newChild);
419
- }
420
- element.insertBefore(newChild, oldNode.nextSibling);
339
+ // 关键修复:即使 newChild === oldChild,如果它是元素,h() 应该已经调用了 updateElement 来更新其子元素
340
+ // 但是,为了确保子元素确实被更新了,我们不应该跳过,而是让后续代码确保元素在正确位置
341
+ // 如果 h() 已经正确更新了子元素,那么这里只需要确保元素在正确位置即可
342
+ if (
343
+ newChild === oldChild &&
344
+ (newChild instanceof HTMLElement || newChild instanceof SVGElement)
345
+ ) {
346
+ // 同一个元素引用,h() 应该已经通过 updateElement 更新了其子元素
347
+ // 但是,如果 cacheManager 可用,我们可以验证并确保子元素确实被更新了
348
+ if (cacheManager) {
349
+ const childMetadata = cacheManager.getMetadata(newChild);
350
+ if (childMetadata) {
351
+ // 如果元数据存在,说明 h() 已经更新了子元素
352
+ // 只需要确保元素在正确位置
353
+ if (oldNode === newChild && newChild.parentNode === element) {
354
+ // 元素已经在正确位置,且 h() 应该已经更新了其子元素
355
+ // 不需要额外处理
356
+ continue;
421
357
  }
422
358
  }
423
359
  } else {
424
- // oldNode 不存在(oldChild 不在 DOM 中),直接添加新元素
425
- if (newChild.parentNode !== element) {
426
- // 如果 newChild 在其他父元素中,先移除
427
- if (newChild.parentNode) {
428
- newChild.parentNode.removeChild(newChild);
429
- }
430
- element.appendChild(newChild);
360
+ // 如果没有 cacheManager,假设 h() 已经更新了子元素
361
+ if (oldNode === newChild && newChild.parentNode === element) {
362
+ continue;
431
363
  }
432
364
  }
433
- } else {
434
- // 类型变化:元素 -> 文本
435
- if (oldNode && !shouldPreserveElement(oldNode)) {
436
- element.removeChild(oldNode);
365
+ // 如果位置不对,需要调整
366
+ }
367
+
368
+ if (newChild instanceof HTMLElement || newChild instanceof SVGElement) {
369
+ // 如果 newChild 已经在 DOM 中且位置正确,不需要替换
370
+ if (newChild.parentNode === element && oldNode === newChild) {
371
+ continue; // 元素已经在正确位置,不需要更新
437
372
  }
373
+ replaceOrInsertElement(element, newChild, oldNode);
374
+ } else {
375
+ // 类型变化:元素 -> 文本/Fragment
376
+ removeNodeIfNotPreserved(element, oldNode);
438
377
  if (typeof newChild === "string" || typeof newChild === "number") {
439
378
  const newTextNode = document.createTextNode(String(newChild));
440
379
  element.insertBefore(newTextNode, oldNode?.nextSibling || null);
@@ -447,158 +386,25 @@ export function updateChildren(
447
386
 
448
387
  // 添加新子节点
449
388
  for (let i = minLength; i < flatNew.length; i++) {
450
- const newChild = flatNew[i];
451
- if (newChild === null || newChild === undefined || newChild === false) {
452
- continue;
453
- }
454
-
455
- if (typeof newChild === "string" || typeof newChild === "number") {
456
- element.appendChild(document.createTextNode(String(newChild)));
457
- } else if (newChild instanceof HTMLElement || newChild instanceof SVGElement) {
458
- // 确保子元素正确添加到当前父容器
459
- // 如果 newChild 已经在 DOM 中,需要检查它是否在正确的位置
460
- if (newChild.parentNode === element) {
461
- // newChild 已经在当前 element 中
462
- // 检查它是否在正确的位置(应该在最后,因为这是"添加新子节点"部分)
463
- const currentIndex = Array.from(element.childNodes).indexOf(newChild);
464
- const expectedIndex = element.childNodes.length - 1;
465
- if (currentIndex !== expectedIndex) {
466
- // 位置不对,移动到正确位置(末尾)
467
- element.removeChild(newChild);
468
- element.appendChild(newChild);
469
- }
470
- // 如果位置正确,跳过(避免重复添加)
471
- // 注意:元素内容会在 updateElement 中更新,所以这里只需要确保位置正确
472
- continue;
473
- } else if (newChild.parentNode) {
474
- // newChild 在其他父元素中,先移除
475
- newChild.parentNode.removeChild(newChild);
476
- }
477
- // 添加 newChild 到当前 element 的末尾
478
- element.appendChild(newChild);
479
- } else if (newChild instanceof DocumentFragment) {
480
- element.appendChild(newChild);
481
- }
389
+ appendNewChild(element, flatNew[i]);
482
390
  }
483
391
 
484
- // 移除多余子节点(阶段 5:正确处理元素保留)
485
- // 关键:需要跳过"应该保留"的元素(第三方库注入的元素)
486
- // 以及已经在 newChildren 中的元素(通过元素引用或 cache key 匹配)
487
- const nodesToRemove: Node[] = [];
488
- const newChildSet = new Set<HTMLElement | SVGElement | DocumentFragment>();
489
- const newChildCacheKeyMap = new Map<string, HTMLElement | SVGElement>();
490
- // 只将元素节点添加到 Set 中(文本节点不能直接比较)
491
- for (const child of flatNew) {
492
- if (
493
- child instanceof HTMLElement ||
494
- child instanceof SVGElement ||
495
- child instanceof DocumentFragment
496
- ) {
497
- newChildSet.add(child);
498
- // 同时收集 cache key 到 Map,用于匹配(即使元素引用不同,cache key 相同也认为是同一个元素)
499
- // 注意:DocumentFragment 没有 cache key,只能通过引用匹配
500
- if (child instanceof HTMLElement || child instanceof SVGElement) {
501
- const cacheKey = getElementCacheKey(child);
502
- if (cacheKey) {
503
- // 如果 cache key 已存在,保留最新的元素(newChild)
504
- newChildCacheKeyMap.set(cacheKey, child);
505
- }
506
- }
507
- }
508
- }
392
+ // 移除多余子节点(使用纯函数简化逻辑)
393
+ // 步骤 1: 构建新子元素的引用集合和 cache key 映射
394
+ const { elementSet, cacheKeyMap } = buildNewChildrenMaps(flatNew);
509
395
 
510
- // 第一步:处理重复的 cache key(如果 DOM 中有多个元素具有相同的 cache key,只保留 newChild)
511
- // 注意:需要从后往前遍历,避免在循环中修改 DOM 导致索引问题
512
- // 关键:这个步骤在"更新现有子节点"循环之后执行,所以需要处理那些在"更新现有子节点"循环中没有处理的重复元素
513
- const processedCacheKeys = new Set<string>();
514
- // 构建 newChild 到其在 flatNew 中索引的映射,用于确定正确位置
515
- const newChildToIndexMap = new Map<HTMLElement | SVGElement, number>();
516
- for (let i = 0; i < flatNew.length; i++) {
517
- const child = flatNew[i];
518
- if (child instanceof HTMLElement || child instanceof SVGElement) {
519
- newChildToIndexMap.set(child, i);
520
- }
521
- }
396
+ // 步骤 2: 处理重复的 cache key(确保每个 cache key 在 DOM 中只出现一次)
397
+ deduplicateCacheKeys(element, cacheKeyMap);
522
398
 
523
- for (let i = element.childNodes.length - 1; i >= 0; i--) {
524
- const child = element.childNodes[i];
525
- if (child instanceof HTMLElement || child instanceof SVGElement) {
526
- // 关键修复:跳过应该保留的元素(第三方库注入的元素)
527
- if (shouldPreserveElement(child)) {
528
- continue;
529
- }
530
- const cacheKey = getElementCacheKey(child);
531
- if (
532
- cacheKey &&
533
- newChildCacheKeyMap.has(cacheKey) &&
534
- !processedCacheKeys.has(cacheKey)
535
- ) {
536
- processedCacheKeys.add(cacheKey);
537
- const newChild = newChildCacheKeyMap.get(cacheKey)!;
538
- // 如果 child 不是 newChild,说明是旧元素,应该被移除或替换
539
- if (child !== newChild) {
540
- // 如果 newChild 已经在 DOM 中,移除旧元素
541
- if (newChild.parentNode === element) {
542
- // newChild 已经在 DOM 中,移除旧元素
543
- // 注意:newChild 已经在 DOM 中,所以不需要再次添加
544
- // 但需要确保 newChild 在正确的位置(通过 replaceChild 移动到旧元素的位置)
545
- // 这样可以确保 newChild 在正确的位置,而不是在错误的位置
546
- // 但是,如果 newChild 在 child 之后,replaceChild 会先移除 newChild,然后替换 child
547
- // 这会导致 newChild 移动到 child 的位置,这是正确的
548
- // 如果 newChild 在 child 之前,replaceChild 也会将 newChild 移动到 child 的位置
549
- // 所以,无论 newChild 在哪里,replaceChild 都会将其移动到 child 的位置
550
- element.replaceChild(newChild, child);
551
- } else {
552
- // newChild 不在 DOM 中,替换旧元素
553
- element.replaceChild(newChild, child);
554
- }
555
- } else {
556
- // child === newChild,说明是同一个元素,不需要处理
557
- // 但需要确保它在正确的位置(这应该在"更新现有子节点"循环中处理)
558
- }
559
- }
560
- }
561
- }
562
-
563
- // 第二步:移除不在 newChildren 中的元素
564
- for (let i = 0; i < element.childNodes.length; i++) {
565
- const child = element.childNodes[i];
399
+ // 步骤 3: 收集需要移除的节点(跳过保留元素和新子元素)
400
+ const nodesToRemove = collectNodesToRemove(element, elementSet, cacheKeyMap);
566
401
 
567
- // 跳过应该保留的元素(第三方库注入的元素)
568
- if (shouldPreserveElement(child)) {
569
- continue;
570
- }
402
+ // 步骤 4: 批量移除节点(从后往前,避免索引变化)
403
+ removeNodes(element, nodesToRemove);
571
404
 
572
- // 跳过已经在 newChildren 中的元素(通过元素引用或 cache key 匹配)
573
- if (child instanceof HTMLElement || child instanceof SVGElement) {
574
- // 方法 1: 直接元素引用匹配
575
- if (newChildSet.has(child)) {
576
- continue;
577
- }
578
- // 方法 2: cache key 匹配(用于处理语言切换等场景,元素引用可能不同但 cache key 相同)
579
- const cacheKey = getElementCacheKey(child);
580
- if (cacheKey && newChildCacheKeyMap.has(cacheKey)) {
581
- // 已经在第一步处理过了,跳过
582
- continue;
583
- }
584
- } else if (child instanceof DocumentFragment) {
585
- // DocumentFragment 只能通过引用匹配
586
- if (newChildSet.has(child)) {
587
- continue;
588
- }
589
- }
590
-
591
- // 只有不应该保留且不在 newChildren 中的节点才添加到移除列表
592
- nodesToRemove.push(child);
593
- }
594
-
595
- // 统一移除(从后往前移除,避免索引变化)
596
- for (let i = nodesToRemove.length - 1; i >= 0; i--) {
597
- const node = nodesToRemove[i];
598
- if (node.parentNode === element) {
599
- element.removeChild(node);
600
- }
601
- }
405
+ // 步骤 5: 重新插入所有保留的元素到 DOM 末尾
406
+ // 这确保了第三方库注入的元素不会丢失
407
+ reinsertPreservedElements(element, preservedElements);
602
408
  }
603
409
 
604
410
  /**
@@ -629,5 +435,5 @@ export function updateElement(
629
435
  updateProps(element, oldProps, newProps, tag);
630
436
 
631
437
  // 更新 children
632
- updateChildren(element, oldChildren, newChildren);
438
+ updateChildren(element, oldChildren, newChildren, cacheManager);
633
439
  }