Skip to content

Commit b314c08

Browse files
committed
feat: add KeepAlive functionality and demo component
1 parent 900d50d commit b314c08

9 files changed

Lines changed: 140 additions & 26 deletions

File tree

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
"echarts": "^5.6.0",
4747
"i18next": "^24.2.1",
4848
"i18next-browser-languagedetector": "^8.0.2",
49+
"keepalive-for-react": "^3.0.8",
4950
"nprogress": "^0.2.0",
5051
"qs": "^6.14.0",
5152
"react": "^18.3.1",

pnpm-lock.yaml

Lines changed: 49 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/api/System/user.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ export interface userInfoType {
3030

3131
// api接口
3232
const api = {
33-
example: 'api/example', // 示例接口
34-
login: '/api/auth/login', // 用户登录接口
33+
example: '/example', // 示例接口
34+
login: '/auth/login', // 用户登录接口
3535
};
3636

3737
/**

src/layout/index.tsx

Lines changed: 36 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import * as vantIcons from '@react-vant/icons';
22
import { ArrowLeft } from '@react-vant/icons';
3-
import { createElement, useRef } from 'react';
3+
import { KeepAlive, useKeepAliveRef } from 'keepalive-for-react';
4+
import { createElement, useMemo, useRef } from 'react';
45
import { useTranslation } from 'react-i18next';
5-
import { Outlet, useLocation, useNavigate } from 'react-router';
6-
import { CSSTransition } from 'react-transition-group';
6+
import { useLocation, useNavigate, useOutlet } from 'react-router';
7+
import { CSSTransition, TransitionGroup } from 'react-transition-group';
78
import { NavBar, Tabbar } from 'react-vant';
89
import IconifyIcon from '@/components/Icon/IconifyIcon';
910
import { constantRoutes } from '@/routers';
10-
import { filterTabBar, searchRoute } from '@/routers/utils';
11+
import { filterKeepAlive, filterTabBar, searchRoute } from '@/routers/utils';
1112
import { useSettingStore } from '@/store/setting';
1213

1314
const Layout = () => {
@@ -34,7 +35,7 @@ const Layout = () => {
3435
};
3536

3637
// 获取路由对象
37-
const { pathname } = useLocation();
38+
const { pathname, search } = useLocation();
3839
const currentRoute = searchRoute(pathname, constantRoutes);
3940

4041
const isShowNavBar = !currentRoute.meta?.hiddenNavBar;
@@ -54,6 +55,16 @@ const Layout = () => {
5455
// 使用i18n全局函数
5556
const { t } = useTranslation();
5657

58+
const outlet = useOutlet();
59+
60+
const aliveRef = useKeepAliveRef();
61+
62+
const cacheKey = useMemo(() => {
63+
return pathname + search;
64+
}, [pathname, search]);
65+
66+
const cacheKeys = filterKeepAlive(constantRoutes);
67+
5768
return (
5869
<div className="h-screen flex flex-col">
5970
{isShowNavBar && (
@@ -71,19 +82,26 @@ const Layout = () => {
7182
/>
7283
)}
7384

74-
<div className="relative flex-1 overflow-x-hidden">
75-
<CSSTransition
76-
key={pathname}
77-
nodeRef={nodeRef}
78-
appear
79-
in={isPageAnimate}
80-
timeout={400}
81-
classNames={getTransitionName()}
82-
>
83-
<div ref={nodeRef} className="absolute inset-0 w-full">
84-
<Outlet />
85-
</div>
86-
</CSSTransition>
85+
<div className="relative flex-1 overflow-hidden">
86+
<KeepAlive activeCacheKey={cacheKey} aliveRef={aliveRef} include={cacheKeys}>
87+
<TransitionGroup component={null}>
88+
<CSSTransition
89+
key={pathname}
90+
nodeRef={nodeRef}
91+
appear
92+
in={isPageAnimate}
93+
timeout={400}
94+
classNames={getTransitionName()}
95+
>
96+
<div
97+
ref={nodeRef}
98+
className="absolute inset-0 w-full overflow-x-hidden overflow-y-auto"
99+
>
100+
{outlet}
101+
</div>
102+
</CSSTransition>
103+
</TransitionGroup>
104+
</KeepAlive>
87105
</div>
88106

89107
{isShowTabBar && (

src/main.tsx

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { StrictMode } from 'react';
21
import { createRoot } from 'react-dom/client';
32
import App from './App.tsx';
43
import { setupI18n } from './locales'; // 引入国际化配置
@@ -12,11 +11,7 @@ function bootstrap() {
1211
const container = document.getElementById('root');
1312
if (!container) return;
1413
const root = createRoot(container);
15-
root.render(
16-
<StrictMode>
17-
<App />
18-
</StrictMode>
19-
);
14+
root.render(<App />);
2015
}
2116

2217
bootstrap();

src/routers/index.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,11 @@ export const constantRoutes: RouteObjectType[] = [
101101
element: lazyLoad(lazy(() => import('@/views/Example/iconDemo'))),
102102
meta: { title: 'Icon 示例', i18n: 'icon' },
103103
},
104+
{
105+
path: '/keepAlive',
106+
element: lazyLoad(lazy(() => import('@/views/Example/keepAliveDemo'))),
107+
meta: { title: 'KeepAlive 演示', i18n: 'keepAlive', keepAlive: true },
108+
},
104109
],
105110
},
106111
notFoundRouter,

src/routers/utils/index.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,24 @@ export const searchRoute = (path: string, routes: RouteObjectType[] = []): Route
3838
}
3939
return result;
4040
};
41+
42+
/**
43+
* 过滤需要缓存的路由
44+
* @param routers 异步路由表
45+
* @returns 需要缓存的路由数组
46+
*/
47+
export const filterKeepAlive = (routers: RouteObjectType[]) => {
48+
const cacheRouter: string[] = [];
49+
const deep = (routerList: RouteObjectType[]) => {
50+
routerList.forEach((item) => {
51+
if (item.meta?.keepAlive && item.path) {
52+
cacheRouter.push(item.path as string);
53+
}
54+
if (item.children && item.children.length) {
55+
deep(item.children);
56+
}
57+
});
58+
};
59+
deep(routers);
60+
return cacheRouter;
61+
};

src/views/Example/index.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ const Example = () => {
1717
{ title: `📊 ${t('route.echarts')}`, route: 'echarts' },
1818
{ title: `🎨 ${t('route.icon')}`, route: 'icon' },
1919
{ title: `🙅 ${t('route.notFound')}`, route: '404' },
20+
{ title: `🧡 ${t('route.keepAlive')}`, route: 'keepAlive' },
2021
],
2122
[t]
2223
);
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { useState } from 'react';
2+
import { useTranslation } from 'react-i18next';
3+
import { Cell, Field, Stepper } from 'react-vant';
4+
5+
const KeepAliveDemo = () => {
6+
// 使用i18n全局函数
7+
const { t } = useTranslation();
8+
9+
const [text, setText] = useState('');
10+
const [value, setValue] = useState(0);
11+
12+
return (
13+
<div className="box-border w-full p-20">
14+
<Cell.Group border={false} className="mb-10 w-full">
15+
<Field value={text} label="文本" placeholder="请输入文本" onChange={setText} />
16+
</Cell.Group>
17+
<Stepper defaultValue={value} onChange={(v) => setValue(v!)} />
18+
19+
<div className="mt-20 color-[var(--van-text-color-2)]">{t('example.keepAliveTips')}</div>
20+
</div>
21+
);
22+
};
23+
24+
export default KeepAliveDemo;

0 commit comments

Comments
 (0)