React.memo 实现原理解析
作者:维维酱
React.memo通过props浅比较或自定义函数,结合Fiber优化机制,跳过组件渲染及副作用,下面就来详细的介绍一下,感兴趣的可以了解一下
核心实现概念
React.memo
的本质是一个高阶组件(HOC),它通过比较前后 props 来决定是否跳过组件的重新渲染。
1. 基本结构
在 React 源码中,React.memo
的实现大致如下:
function memo(type, compare) { // 创建一个特殊的元素类型 const elementType = { $$typeof: REACT_MEMO_TYPE, // 特殊标识符,表示这是一个 memo 组件 type, // 被包裹的原始组件 compare: compare === undefined ? null : compare, // 自定义比较函数 }; return elementType; }
2. 在协调(Reconciliation)过程中的处理
当 React 遇到 REACT_MEMO_TYPE
类型的元素时,会执行特殊处理:
// 简化版的协调逻辑 function updateMemoComponent( currentFiber, // 当前 Fiber 节点 workInProgressFiber, // 工作中的 Fiber 节点 Component, // 被 memo 包裹的组件 nextProps, // 新的 props renderLanes // 渲染优先级 ) { // 获取之前的 props const current = currentFiber.memoizedProps; // 决定使用哪种比较函数 let compare = Component.compare; if (compare === null) { // 默认使用浅比较 compare = shallowCompare; } // 检查 props 是否相等 if (compare(current, nextProps)) { // Props 没有变化,完全跳过渲染 // 复用之前的子节点 return bailoutOnAlreadyFinishedWork( currentFiber, workInProgressFiber, renderLanes ); } // Props 发生了变化,继续正常渲染流程 return updateFunctionComponent( currentFiber, workInProgressFiber, Component, nextProps, renderLanes ); }
深入实现细节
1. 浅比较 (shallowCompare) 的实现
React 使用的默认浅比较函数类似于:
function shallowCompare(prevProps, nextProps) { // 如果 props 对象引用相同,直接返回 true if (prevProps === nextProps) { return true; } // 检查 props 键的数量 const prevKeys = Object.keys(prevProps); const nextKeys = Object.keys(nextProps); if (prevKeys.length !== nextKeys.length) { return false; } // 逐个比较每个属性值 for (let i = 0; i < prevKeys.length; i++) { const key = prevKeys[i]; // 使用 Object.is 进行严格比较(类似于 ===,但处理了 NaN 和 +0/-0 的情况) if (!Object.is(prevProps[key], nextProps[key])) { return false; } } return true; }
2. Fiber 架构中的优化机制
在 React 的 Fiber 架构中,React.memo
的优化是通过以下机制实现的:
- 提前中止渲染:在
beginWork
阶段,如果检测到是 memo 组件且 props 未变化,React 会立即中止当前组件的渲染工作。 - 子树复用:通过
bailoutOnAlreadyFinishedWork
函数,React 会标记整个子树为"无需更新",直接复用之前的渲染结果。 - 跳过副作用:由于组件没有重新渲染,相关的副作用(如 useEffect)也不会被触发。
3. 与 React 渲染流程的整合
// 简化的渲染流程 function beginWork(currentFiber, workInProgressFiber, renderLanes) { // 检查是否是 memo 组件 if (workInProgressFiber.type && workInProgressFiber.type.$$typeof === REACT_MEMO_TYPE) { return updateMemoComponent( currentFiber, workInProgressFiber, workInProgressFiber.type.type, // 提取原始组件 workInProgressFiber.pendingProps, renderLanes ); } // 处理其他类型的组件... }
性能考虑与实现优化
1. 记忆化策略
React.memo
的实现采用了记忆化(Memoization)策略:
- 存储之前的结果:React 会存储组件上一次的渲染结果(在 Fiber 节点的
memoizedState
和memoizedProps
中) - 比较成本与渲染成本的权衡:浅比较的计算成本远低于组件的渲染成本(包括虚拟 DOM 创建和差异比较)
2. 选择性优化
React 不会对所有组件都应用 memo 优化,因为:
- 比较本身有成本
- 对于频繁更新或 props 经常变化的组件,memo 可能带来负收益
3. 与其他优化机制的协同
React.memo
与 React 的其他优化机制协同工作:
- Context 优化:即使使用
React.memo
,如果组件消费的 Context 值发生变化,组件仍会重新渲染 - 状态更新优化:组件内部的状态更新不受
React.memo
影响
实际应用中的实现考虑
1. 自定义比较函数的高级用法
// 深度比较实现(不推荐在生产环境使用,仅作示例) function deepCompare(prevProps, nextProps) { return JSON.stringify(prevProps) === JSON.stringify(nextProps); } // 选择性比较 function selectiveCompare(prevProps, nextProps) { // 只比较我们关心的属性 return prevProps.importantValue === nextProps.importantValue; } const ExpensiveComponent = React.memo( function ExpensiveComponent(props) { // 组件实现 }, selectiveCompare // 使用自定义比较函数 );
2. 与 Hooks 的交互
React.memo
的实现需要考虑与 Hooks 的交互:
function MyComponent(props) { // 即使使用 React.memo,内部状态变化仍会导致重新渲染 const [state, setState] = useState(0); // 使用 useMemo 和 useCallback 可以进一步优化 const computedValue = useMemo(() => { return expensiveCalculation(props.someValue); }, [props.someValue]); const handleClick = useCallback(() => { // 处理点击 }, []); return <div>{/* ... */}</div>; } export default React.memo(MyComponent);
总结
React.memo
的实现原理可以概括为:
- 高阶组件包装:通过创建特殊类型的 React 元素标记 memo 组件
- 协调阶段拦截:在协调过程中识别 memo 组件并执行特殊处理
- Props 比较:使用浅比较或自定义比较函数判断 props 是否变化
- 渲染优化:如果 props 未变化,跳过组件的渲染和子树的协调过程
- 结果复用:直接复用之前的渲染结果,避免不必要的计算和 DOM 操作
这种实现方式体现了 React 性能优化的核心思想:用较小的比较成本换取可能很大的渲染成本节约。
到此这篇关于React.memo 实现原理解析的文章就介绍到这了,更多相关React.memo实现内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!