React 的 useMemo 是怎么实现的?你自己能造一个吗?
- 工作日记
- 4天前
- 34热度
- 0评论
React useMemo实现原理深度解析:从源码到手动实现
当Vue开发者初次接触React的useMemo时,往往会下意识地将其等同于computed属性。但当我们深入源码时会发现:useMemo本质上是一个带有依赖追踪的计算结果缓存系统,它更像是经过精密设计的备忘录而非响应式魔法。本文将带您穿透表象,解密其核心实现机制,并亲手打造一个简易版useMemo。
一、React useMemo的三大核心实现原理
1.1 Hooks的链表存储结构
React通过单向链表结构存储Hooks状态,每个useMemo对应链表中的一个节点。在组件首次渲染时创建包含三个关键属性的节点:
- memoizedState:缓存的计算结果
- dependencies:依赖项数组
- queue:更新队列(用于调度重计算)
1.2 依赖对比算法
React采用浅比较(shallow compare)进行依赖项比对,其核心逻辑可简化为:
function areDependenciesEqual(prevDeps, nextDeps) { for(let i=0; i1.3 缓存更新策略
当检测到依赖变化时,React会触发同步重计算而非异步更新。源码中的核心逻辑伪代码如下:
function updateMemo(create, deps) { const hook = updateWorkInProgressHook(); const nextDeps = deps === undefined ? null : deps; if (hook.memoizedState !== null) { const prevState = hook.memoizedState; if (areDependenciesEqual(prevState.dependencies, nextDeps)) { return prevState.memoizedValue; } } const nextValue = create(); hook.memoizedState = { memoizedValue: nextValue, dependencies: nextDeps }; return nextValue; }二、手动实现一个简易版useMemo
2.1 存储系统设计
我们使用闭包变量模拟React的Hooks存储机制:
let memoCache = []; let cursor = 0; function resetCursor() { cursor = 0; }2.2 核心逻辑实现
function myUseMemo(create, deps) { // 初始化缓存节点 if (!memoCache[cursor]) { memoCache[cursor] = { value: create(), deps: deps }; cursor++; return memoCache[cursor到1].value; } // 依赖比对 const shouldRecompute = deps.some((dep, i) => !Object.is(dep, memoCache[cursor].deps[i]) ); if (shouldRecompute) { memoCache[cursor] = { value: create(), deps: deps }; } cursor++; return memoCache[cursor-1].value; }2.3 与React实现的关键差异
- 存储结构差异:使用数组替代链表,简化状态管理
- 渲染周期处理:缺少React的渲染队列调度机制
- 依赖处理:未实现React的自动依赖收集功能
三、使用useMemo的五个黄金准则
3.1 必须使用的场景
- 复杂计算缓存:如矩阵运算、大数据量转换
// 计算斐波那契数列 const fib = useMemo(() => { let a=0, b=1; for(let i=0; i<100000; i++) [a,b] = [b, a+b]; return b; }, []);组件渲染优化:避免无效的子组件重渲染 const memoizedChild = useMemo(() =>, [data]); 3.2 必须警惕的陷阱
- 过早优化反噬:简单计算使用useMemo反而增加内存开销
- 依赖数组陷阱:忘记更新依赖项会导致缓存失效
- 副作用滥用:在useMemo中执行API调用等副作用操作
3.3 性能评估策略
console.time('expensiveCalc'); // 需要缓存的复杂计算 console.timeEnd('expensiveCalc'); // 超过1ms的计算才值得缓存四、从原理到实践的正确认知
通过源码分析我们发现,useMemo本质上是一个依赖驱动的缓存系统,而非响应式状态管理工具。其实现亮点在于:
- 与React渲染流程深度集成的更新机制
- 基于Object.is的精确依赖比较算法
- 链表结构带来的多Hook协同能力
当我们在日常开发中遇到以下场景时,才是useMemo真正该登场的时候:
- 计算耗时超过1ms的复杂逻辑
- 需要保持引用稳定的对象/数组
- 高频交互组件的渲染优化
记住:性能优化不是银弹,useMemo更不是装饰品。 只有理解其实现原理,才能让这个工具真正为你的应用加速。