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