React 怎样实现类似 Vue keep-alive 的组件缓存?
先理解原理和示例,再练习完整口述与追问。
回答要点
React 卸载组件就会丢失状态,没有内置 keep-alive。常见方案:用 CSS 隐藏而不是卸载;把需要保留的状态提升到父组件、全局 store 或 URL;React 19.2 起使用官方 <Activity mode="hidden">,隐藏时保留状态与 DOM 并卸载 Effects。社区方案 react-activation 借助 Portal 实现,但与 Context、新版 React 存在兼容风险。
原理拆解
React 根据组件在树中的位置与类型维护状态,条件渲染为 false 时 Fiber 被删除,状态随之销毁。最简单的保留方式是始终渲染、用 hidden 或 display: none 隐藏,代价是隐藏组件仍参与更新、占用内存。
<Activity> 的 hidden 模式会隐藏子树、清理其中的 Effects(如定时器、订阅),状态保留;切换回 visible 时恢复 Effects,并以较低优先级预渲染隐藏内容。列表页返回时恢复滚动位置,也可以只保存数据和 scrollTop,配合 React Query 缓存实现“看起来没刷新”。
示例与推演
import { Activity, useState } from 'react'
function Tabs() {
const [tab, setTab] = useState('list')
return (
<>
<Activity mode={tab === 'list' ? 'visible' : 'hidden'}><OrderList /></Activity>
<Activity mode={tab === 'chart' ? 'visible' : 'hidden'}><SalesChart /></Activity>
</>
)
}
切到图表再切回,列表的筛选条件、滚动位置和已输入内容仍在。
易错点
缓存的页面过多会占用大量内存,需要上限与淘汰策略;隐藏组件依赖的数据可能已过期,重新显示时要判断是否刷新。依赖 Effects 持续运行的逻辑(如 WebSocket)在隐藏后会断开,需要移到外层。
参考回答可直接练习口述
React 卸载组件就丢状态,没有内置 keep-alive。简单场景可以不卸载,用 CSS 隐藏;也可以把状态提升到父组件、store 或 URL,列表配合 React Query 缓存加恢复滚动位置。React 19.2 有了官方的 Activity 组件,hidden 时保留状态和 DOM,并清理 Effects,显示时再恢复。无论哪种方案都要控制缓存数量,并在重新显示时判断数据是否过期。