一个强大的 React 路由缓存组件库,专为 React Router v6+ 设计,提供灵活的页面状态保持和生命周期管理。了解更多见:
- 🔥 智能生命周期:
useActiveEffect提供类似useEffect的 API,支持激活/失活回调 - 📦 轻量级设计:核心代码精简,性能优异,零依赖冲突
- 🔧 完整类型支持:100% TypeScript 编写,提供完整的类型定义
- 🎯 开箱即用:
KeepAliveOutlet组件,零配置集成路由缓存 - 🔄 灵活控制:支持缓存刷新、销毁、状态查询等完整缓存管理
- 📊 实时监控:提供缓存数量、状态等实时信息查询
- 🎛️ 可配置:支持最大缓存数量、清理策略等个性化配置
- 表单页面:保持用户填写的表单数据,避免意外丢失
- 列表页面:保持滚动位置、筛选条件、分页状态
- 详情页面:避免重复网络请求,提升用户体验
- 多标签页应用:实现标签页之间的快速切换和管理
- 复杂状态管理:保持组件内部状态,如计时器、WebSocket 连接等
- 工作流应用:保持用户在不同步骤之间的操作进度
npm install @feoe/react-keepalive
# 或
yarn add @feoe/react-keepalive
# 或
pnpm add @feoe/react-keepalive路由级别的缓存组件,用于替代 react-router-dom 的 <Outlet>。
import { KeepAliveOutlet } from '@feoe/react-keepalive';
function Layout() {
return (
<div>
<nav>导航栏</nav>
<KeepAliveOutlet
maxCacheSize={20}
cleanOnUnmount={true}
/>
</div>
);
}Props:
maxCacheSize?: number- 最大缓存数量,默认 20cleanOnUnmount?: boolean- 组件卸载时是否清理所有缓存,默认 truekeepAliveRef?: React.RefObject<KeepAliveRef>- 外部传入的 KeepAlive 引用,用于在组件外部控制缓存
缓存上下文提供者,为子组件提供缓存管理功能。
import { KeepAliveProvider } from '@feoe/react-keepalive';
function App() {
return (
<KeepAliveProvider activeKey="/current-route" keepAliveRef={keepAliveRef}>
{/* 子组件 */}
</KeepAliveProvider>
);
}核心缓存组件,可直接使用进行精细化缓存控制。
import { KeepAlive } from '@feoe/react-keepalive';
function CustomKeepAlive() {
return (
<KeepAlive activeKey="current-key" maxCacheSize={5}>
{/* 需要缓存的内容 */}
</KeepAlive>
);
}类似 useEffect 的生命周期管理钩子,专为 KeepAlive 组件设计。
import { useActiveEffect } from '@feoe/react-keepalive';
function MyComponent() {
useActiveEffect(() => {
console.log('组件被激活');
// 返回清理函数(在组件失活时执行)
return () => {
console.log('组件失活,执行清理');
};
});
return <div>我的组件</div>;
}执行时机:
- 组件首次挂载时:
useEffect执行 →useActiveEffect执行 - 从缓存中恢复时:
useActiveEffect执行 - 组件失活时:
useActiveEffect返回的清理函数执行 - 组件销毁时:
useActiveEffect返回的清理函数执行 →useEffect的清理函数执行
获取 KeepAlive 实例引用,提供完整的缓存控制方法。
import { useKeepAliveRef } from '@feoe/react-keepalive';
function CacheManager() {
const {
refresh,
destroy,
destroyAll,
destroyOther,
isCached,
getCacheKeys,
getCacheSize
} = useKeepAliveRef();
return (
<div>
<p>缓存数量: {getCacheSize()}</p>
<button onClick={() => refresh()}>刷新当前页面</button>
<button onClick={() => destroy()}>销毁当前缓存</button>
<button onClick={() => destroyOther()}>销毁其他缓存</button>
<button onClick={() => destroyAll()}>清空所有缓存</button>
</div>
);
}返回方法的接口定义:
export interface KeepAliveRef {
/**
* 刷新指定 key 的缓存(保持组件状态)
*/
refresh: (key?: string) => void;
/**
* 清理指定 key 的缓存
*/
destroy: (key?: string | string[]) => void;
/**
* 清理所有除 key 之外的其他缓存
*/
destroyOther: (key?: string) => void;
/**
* 清理所有缓存
*/
destroyAll: () => void;
/**
* 是否已经缓存
*/
isCached: (key?: string) => boolean;
/**
* 获取当前缓存的 keys
*/
getCacheKeys: () => string[];
/**
* 获取缓存数量
*/
getCacheSize: () => number;
}获取 KeepAlive 上下文,用于底层控制和扩展。
import { useKeepAliveContext } from '@feoe/react-keepalive';
function AdvancedComponent() {
const { activeKey, keepAliveRef } = useKeepAliveContext();
return <div>当前活跃路由: {activeKey}</div>;
}返回方法的接口定义:
export interface IKeepAliveContext {
activeKey: string;
keepAliveRef: RefObject<KeepAliveRef>;
// 注册/注销回调函数
registerActivatedCallback: (
key: string,
callback: () => void,
) => () => void;
registerDeactivatedCallback: (
key: string,
callback: () => void,
) => () => void;
// 触发回调函数
triggerActivatedCallbacks: (key: string) => void;
triggerDeactivatedCallbacks: (key: string) => void;
}const { refresh } = useKeepAliveRef();
// 刷新当前路由缓存(保持组件状态,触发重新渲染)
refresh();
// 刷新指定路由缓存
refresh('/specific-route');const { isCached, getCacheKeys, getCacheSize } = useKeepAliveRef();
// 检查特定路由是否已缓存
if (isCached('/some-route')) {
console.log('该路由已被缓存');
}
// 获取所有缓存的路由列表
const allCachedRoutes = getCacheKeys();
console.log('已缓存的路由:', allCachedRoutes);
// 获取当前缓存数量
const cacheCount = getCacheSize();
console.log('当前缓存数量:', cacheCount);// 批量销毁多个缓存
const { destroy } = useKeepAliveRef();
destroy(['/route1', '/route2', '/route3']);
// 销毁除当前路由外的所有其他缓存
const { destroyOther } = useKeepAliveRef();
destroyOther(); // 不传参数则保留当前路由
destroyOther('/route-to-keep'); // 传参数则保留指定路由查看 examples/keep-alive-transition 目录中的完整示例,包含:
- 基础 KeepAlive 用法演示
- 表单状态保持示例
- 列表滚动位置保持
- 缓存管理功能展示
- 生命周期钩子使用方法
- 批量操作和高级功能演示
运行示例:
cd examples/keep-alive-transition
npm install
npm run dev最简单的使用方式,替换 <Outlet> 为 <KeepAliveOutlet>:
import { KeepAliveOutlet } from '@feoe/react-keepalive';
import { createBrowserRouter, RouterProvider, Link } from 'react-router-dom';
const router = createBrowserRouter([
{
path: '/',
element: <Layout />,
children: [
{ path: 'home', element: <HomePage /> },
{ path: 'about', element: <AboutPage /> },
{ path: 'contact', element: <ContactPage /> },
],
},
]);
function Layout() {
return (
<div>
<nav>
<Link to="/home">首页</Link>
<Link to="/about">关于</Link>
<Link to="/contact">联系</Link>
</nav>
{/* 使用 KeepAliveOutlet 替代 Outlet */}
<KeepAliveOutlet maxCacheSize={20} />
</div>
);
}
function App() {
return <RouterProvider router={router} />;
}在需要缓存的页面组件中使用 useActiveEffect:
import { useActiveEffect, useKeepAliveRef } from '@feoe/react-keepalive';
import { useState, useRef } from 'react';
function FormPage() {
const [formData, setFormData] = useState({ name: '', email: '' });
const [count, setCount] = useState(0);
const timerRef = useRef<NodeJS.Timeout>();
const { isCached } = useKeepAliveRef();
useActiveEffect(() => {
console.log('页面激活,开始计时器');
// 启动计时器
timerRef.current = setInterval(() => {
setCount(prev => prev + 1);
}, 1000);
// 返回清理函数
return () => {
console.log('页面失活,清理计时器');
if (timerRef.current) {
clearInterval(timerRef.current);
}
};
});
return (
<div>
<h2>表单页面</h2>
<p>是否已缓存: {isCached() ? '是' : '否'}</p>
<p>计数器: {count} (激活时递增,失活时停止)</p>
<form>
<input
type="text"
placeholder="姓名"
value={formData.name}
onChange={(e) => setFormData(prev => ({ ...prev, name: e.target.value }))}
/>
<input
type="email"
placeholder="邮箱"
value={formData.email}
onChange={(e) => setFormData(prev => ({ ...prev, email: e.target.value }))}
/>
</form>
</div>
);
}创建一个缓存管理页面:
import { useActiveEffect, useKeepAliveRef } from '@feoe/react-keepalive';
import { useState } from 'react';
function CacheManagerPage() {
const [cacheList, setCacheList] = useState<string[]>([]);
const {
getCacheKeys,
getCacheSize,
refresh,
destroy,
destroyAll,
destroyOther,
isCached
} = useKeepAliveRef();
const refreshCacheList = () => {
setCacheList(getCacheKeys());
};
useActiveEffect(() => {
console.log('缓存管理页面激活');
refreshCacheList();
return () => {
console.log('缓存管理页面失活');
};
});
return (
<div>
<h2>缓存管理中心</h2>
<p>总缓存数量: {getCacheSize()}</p>
<div style={{ marginBottom: '16px' }}>
<button onClick={refreshCacheList} style={{ marginRight: '8px' }}>
刷新列表
</button>
<button onClick={() => destroyAll()} style={{ marginRight: '8px' }}>
清空所有缓存
</button>
<button onClick={() => destroyOther()}>
销毁其他缓存
</button>
</div>
<h3>缓存列表:</h3>
{cacheList.map(key => (
<div key={key} style={{
margin: '8px 0',
padding: '8px',
border: '1px solid #ccc',
borderRadius: '4px'
}}>
<span style={{ marginRight: '8px' }}>{key}</span>
<span style={{
color: isCached(key) ? 'green' : 'gray',
marginRight: '8px'
}}>
{isCached(key) ? '已缓存' : '未缓存'}
</span>
<button onClick={() => refresh(key)} style={{ marginRight: '8px' }}>
刷新
</button>
<button onClick={() => destroy(key)}>
删除
</button>
</div>
))}
{/* 批量操作示例 */}
<div style={{ marginTop: '16px' }}>
<h4>批量操作:</h4>
<button onClick={() => destroy(cacheList.slice(0, 2))}>
删除前两个缓存
</button>
</div>
</div>
);
}在某些场景下,您可能需要在 KeepAliveOutlet 外部控制缓存,比如实现 Tabs 功能:
import { KeepAliveOutlet, KeepAliveRef } from '@feoe/react-keepalive';
import { useRef } from 'react';
function TabsLayout() {
const keepAliveRef = useRef<KeepAliveRef>(null);
const handleCloseTab = (tabKey: string) => {
// 在外部控制缓存销毁
keepAliveRef.current?.destroy(tabKey);
};
const handleCloseOtherTabs = (activeKey: string) => {
// 销毁除当前标签外的其他缓存
keepAliveRef.current?.destroyOther(activeKey);
};
return (
<div>
<div className="tabs">
{/* 标签页头部 */}
<button onClick={() => handleCloseTab('/some-route')}>
关闭标签
</button>
<button onClick={() => handleCloseOtherTabs('/current-route')}>
关闭其他标签
</button>
</div>
{/* 传入外部的 keepAliveRef */}
<KeepAliveOutlet
keepAliveRef={keepAliveRef}
maxCacheSize={20}
/>
</div>
);
}欢迎提交 Issue 和 Pull Request!
- Fork 本仓库
- 创建功能分支:
git checkout -b feature/new-feature - 提交更改:
git commit -am 'Add new feature' - 推送分支:
git push origin feature/new-feature - 提交 Pull Request
MIT License - 详见 LICENSE 文件