|
1 | 1 | import React, { cloneElement, isValidElement } from 'react'; |
2 | 2 |
|
3 | | -const stripProps = (props: Record<string, any>) => { |
4 | | - const result: Record<string, any> = {}; |
| 3 | +// 预定义需要剥离的属性集合,提高查找性能 |
| 4 | +const STRIP_PROPS = new Set(['id', 'ref', 'onFocus', 'onBlur', 'tabIndex']); |
| 5 | + |
| 6 | +// 使用 WeakMap 缓存已处理的 props,避免重复计算 |
| 7 | +const propsCache = new WeakMap<Record<string, unknown>, Record<string, unknown>>(); |
| 8 | + |
| 9 | +const stripProps = (props: Record<string, unknown>) => { |
| 10 | + // 检查缓存 |
| 11 | + const cached = propsCache.get(props); |
| 12 | + if (cached) { |
| 13 | + return cached; |
| 14 | + } |
| 15 | + |
| 16 | + let hasChanges = false; |
| 17 | + const result: Record<string, unknown> = {}; |
| 18 | + |
5 | 19 | for (const key in props) { |
6 | | - // strip data-*、id、ref, onFocus、onBlur |
7 | | - if ( |
8 | | - key === 'id' || |
9 | | - key === 'ref' || |
10 | | - key === 'onFocus' || |
11 | | - key === 'onBlur' || |
12 | | - key === 'tabIndex' || |
13 | | - key.startsWith('data-') |
14 | | - ) |
| 20 | + // 使用 Set 进行 O(1) 查找,优化 data-* 属性检查 |
| 21 | + if (STRIP_PROPS.has(key) || key.startsWith('data-')) { |
| 22 | + hasChanges = true; |
15 | 23 | continue; |
| 24 | + } |
16 | 25 | result[key] = props[key]; |
17 | 26 | } |
18 | | - return result; |
| 27 | + |
| 28 | + // 如果没有需要剥离的属性,直接返回原对象 |
| 29 | + const finalResult = hasChanges ? result : props; |
| 30 | + |
| 31 | + // 缓存结果 |
| 32 | + propsCache.set(props, finalResult); |
| 33 | + |
| 34 | + return finalResult; |
19 | 35 | }; |
20 | 36 |
|
| 37 | +// 使用 WeakMap 缓存已处理的节点,避免重复处理 |
| 38 | +const nodeCache = new WeakMap<React.ReactElement, React.ReactNode>(); |
| 39 | + |
21 | 40 | /** |
22 | 41 | * Recursively clone ReactNode and remove data-*, id, ref, onFocus, onBlur props |
23 | 42 | * to avoid potential issues with nested elements in table cells. |
| 43 | + * |
| 44 | + * 优化特性: |
| 45 | + * 1. 缓存机制避免重复处理相同节点 |
| 46 | + * 2. 提前退出条件减少不必要的递归 |
| 47 | + * 3. 浅层优化:如果props没有变化,直接返回原节点 |
24 | 48 | */ |
25 | 49 | const sanitizeCloneElement = (node: React.ReactNode): React.ReactNode => { |
26 | 50 | if (!isValidElement(node)) { |
27 | 51 | return node; |
28 | 52 | } |
29 | | - const cleanedProps = stripProps(node.props); |
| 53 | + |
| 54 | + // 检查缓存 |
| 55 | + const cached = nodeCache.get(node); |
| 56 | + if (cached) { |
| 57 | + return cached; |
| 58 | + } |
| 59 | + |
| 60 | + const cleanedProps = stripProps(node.props as Record<string, unknown>); |
| 61 | + |
| 62 | + // 如果props没有变化且没有children需要处理,直接返回原节点 |
| 63 | + if (cleanedProps === node.props && !cleanedProps.children) { |
| 64 | + nodeCache.set(node, node); |
| 65 | + return node; |
| 66 | + } |
| 67 | + |
| 68 | + let processedChildren = cleanedProps.children; |
30 | 69 | if (cleanedProps.children) { |
31 | | - cleanedProps.children = React.Children.map(cleanedProps.children, child => |
| 70 | + processedChildren = React.Children.map(cleanedProps.children as React.ReactNode, child => |
32 | 71 | sanitizeCloneElement(child), |
33 | 72 | ); |
34 | 73 | } |
35 | | - return cloneElement(node, cleanedProps); |
| 74 | + |
| 75 | + const result = cloneElement(node, { |
| 76 | + ...cleanedProps, |
| 77 | + children: processedChildren, |
| 78 | + } as React.Attributes); |
| 79 | + |
| 80 | + // 缓存结果 |
| 81 | + nodeCache.set(node, result); |
| 82 | + |
| 83 | + return result; |
36 | 84 | }; |
37 | 85 |
|
38 | 86 | export { sanitizeCloneElement }; |
0 commit comments