前言
笔者百无聊赖,把项目中的react-router升级到最新版本来体验体验
"react-router": "6.8.1",
"react-router-dom": "6.8.1",
令我惊讶的是,最新版本的router官网甚至连翻译都没有,也许是我没找到,官网还是全英的
因为项目中react是18,router又升级到了6.8这就导致了以前用的react-router-cache-route不起作用了
于是笔者网上找了许久都没找到现成的解决方案,所以就只能自己边看文档边摸索了
最后算是解决了keepAlive,所以希望能帮助到遇到相同问题的同学
正文
说真的,router6.8真的变化很大,很多用法有实质性的改变,感觉router也在往拥抱函数式的路上渐行渐远啊。
先来看下以下代码,用于渲染两个页面A & B
function A() {
const navigate = useNavigate();
return <div>
<h1 onClick={() => navigate('/b')}>A</h1>
<input type="text" />
</div>
}
function B() {
const navigate = useNavigate();
return <div>
<h1 onClick={() => navigate('/a')}>B</h1>
<input type="text" />
</div>
}
// 路由映射表
const routes: RouteObject[] = [
{
path: '/a',
element: <A />,
},
{
path: '/b',
element: <B />,
}
]
const router = createBrowserRouter(routes, {
basename: process.env.PUBLIC_URL
})
function App() {
// const route = useRoutes(routes);
return (
<Suspense fallback={<div />} >
<RouterProvider router={router} />
</Suspense>
);
}
export default App;
个人认为不同点在于应用路由的方式 不同于之前的Switch route
而是采用了 createBrowserRouter RouterProvider useRoutes
具体的用法细节可以参考官方文档哈
上面的代码在页面上的效果是这样的:
我们可以实现A - B之间的切换 但是A& B页面的输入内容是没法保留的
所以我们需要实现keep alive 切换的同时保留我们在页面留下的痕迹
实现 KeepAlive
实现的关键在于router6.8 提供了useOutLet 让我们可以捕获匹配到的子元素
那么我们干脆自己存储子元素 再自行判断路由来做条件渲染就好了
由于要alive当前页面的元素,势必就只能采用样式隐藏的方式 来做 这也是当前社区主流的做法
所以我们根据上面的思路设计存储组件:
import { useUpdate } from "ahooks";
import { useEffect, useRef } from "react";
import { useLocation, useOutlet } from "react-router-dom";
function KeepAlive() {
const componentList = useRef(new Map());
const outLet = useOutlet();
const { pathname } = useLocation();
const forceUpdate = useUpdate();
useEffect(() => {
if (!componentList.current.has(pathname)) {
componentList.current.set(pathname, outLet);
}
forceUpdate();
}, [pathname]);
return <div>
{
Array.from(componentList.current).map(([key, component]) =>
<div key={key} style={{display: pathname === key ? 'block': 'none'}}>
{component}
</div>
)
}
</div>
}
export default KeepAlive;
原理很简单,使用map结构 缓存url -> outlet的映射 然后再循环列表渲染
keepalive组件做好了,我们需要调整routes的结构
// 路由映射表
const routes: RouteObject[] = [
{
path: "/",
element: <KeepAlive />,
children: [
{
path: '/a',
element: <A />,
},
{
path: '/b',
element: <B />,
}
],
}
]
我们在顶层的父级路径使用,后续所有的路由组件都会应用到keep alive功能
现在再来看看效果吧~
所以,keepAlive并不复杂,如果遇到相同的问题,希望对大家能有点帮助吧