feat: 添加 PWA 更新组件,优化缓存策略和动态路由

This commit is contained in:
xiongxiao
2026-03-21 00:42:15 +08:00
committed by cnb
parent 02925f69f4
commit 9d4b3f013a
5 changed files with 112 additions and 2 deletions

View File

@@ -26,7 +26,6 @@
"src/main.tsx": "https://kevisual.cn/root/ai/kevisual/frontend/vite-react-template/src/main.tsx", "src/main.tsx": "https://kevisual.cn/root/ai/kevisual/frontend/vite-react-template/src/main.tsx",
"public/auth.json": "https://kevisual.cn/root/ai/kevisual/frontend/vite-react-template/public/auth.json", "public/auth.json": "https://kevisual.cn/root/ai/kevisual/frontend/vite-react-template/public/auth.json",
"src/agents/index.ts": "https://kevisual.cn/root/ai/kevisual/frontend/vite-react-template/src/agents/index.ts", "src/agents/index.ts": "https://kevisual.cn/root/ai/kevisual/frontend/vite-react-template/src/agents/index.ts",
"src/modules/basename.ts": "https://kevisual.cn/root/ai/kevisual/frontend/vite-react-template/src/modules/basename.ts",
"src/modules/query.ts": "https://kevisual.cn/root/ai/kevisual/frontend/vite-react-template/src/modules/query.ts", "src/modules/query.ts": "https://kevisual.cn/root/ai/kevisual/frontend/vite-react-template/src/modules/query.ts",
"src/routes/demo.tsx": "https://kevisual.cn/root/ai/kevisual/frontend/vite-react-template/src/routes/demo.tsx", "src/routes/demo.tsx": "https://kevisual.cn/root/ai/kevisual/frontend/vite-react-template/src/routes/demo.tsx",
"src/routes/index.tsx": "https://kevisual.cn/root/ai/kevisual/frontend/vite-react-template/src/routes/index.tsx", "src/routes/index.tsx": "https://kevisual.cn/root/ai/kevisual/frontend/vite-react-template/src/routes/index.tsx",

View File

@@ -0,0 +1,60 @@
import { useState } from 'react';
import { useRegisterSW } from 'virtual:pwa-register/react';
import { Button } from '@/components/ui/button';
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from '@/components/ui/card';
function PWAUpdate() {
const {
needRefresh: [needRefresh, setNeedRefresh],
updateServiceWorker,
} = useRegisterSW({
onNeedRefresh() {
setNeedRefresh(true);
},
});
const [isLoading, setIsLoading] = useState(false);
const handleUpdate = async () => {
setIsLoading(true);
await updateServiceWorker(true);
setIsLoading(false);
};
const handleDismiss = () => {
setNeedRefresh(false);
};
if (!needRefresh) {
return null;
}
return (
<div className="fixed bottom-4 right-4 z-50">
<Card className="w-80 shadow-lg">
<CardHeader className="pb-3">
<CardTitle></CardTitle>
<CardDescription></CardDescription>
</CardHeader>
<CardContent className="pt-0" />
<CardFooter className="gap-2">
<Button variant="outline" size="sm" onClick={handleDismiss}>
</Button>
<Button size="sm" onClick={handleUpdate} disabled={isLoading}>
{isLoading ? '更新中...' : '立即更新'}
</Button>
</CardFooter>
</Card>
</div>
);
}
export default PWAUpdate;

View File

@@ -0,0 +1,13 @@
import { cn } from "@/lib/utils"
function Skeleton({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="skeleton"
className={cn("animate-pulse rounded-md bg-muted", className)}
{...props}
/>
)
}
export { Skeleton }

View File

@@ -4,6 +4,7 @@ import { routeTree } from './routeTree.gen'
import './index.css' import './index.css'
import { getDynamicBasename } from './modules/basename' import { getDynamicBasename } from './modules/basename'
import './agents/index.ts'; import './agents/index.ts';
import PWAUpdate from './components/a/PWAUpdate.tsx';
// Set up a Router instance // Set up a Router instance
const router = createRouter({ const router = createRouter({
routeTree, routeTree,
@@ -23,5 +24,10 @@ const rootElement = document.getElementById('root')!
if (!rootElement.innerHTML) { if (!rootElement.innerHTML) {
const root = ReactDOM.createRoot(rootElement) const root = ReactDOM.createRoot(rootElement)
root.render(<RouterProvider router={router} />) root.render(
<>
<RouterProvider router={router} />
<PWAUpdate />
</>
)
} }

View File

@@ -33,6 +33,38 @@ export default defineConfig({
VitePWA({ VitePWA({
injectRegister: 'auto', injectRegister: 'auto',
registerType: 'autoUpdate', registerType: 'autoUpdate',
// Workbox 缓存策略配置
workbox: {
// API 请求使用网络优先策略,确保获取最新数据
runtimeCaching: [
{
urlPattern: /^https?.*\/api\/.*/,
handler: 'NetworkFirst',
options: {
cacheName: 'api-cache',
expiration: {
maxEntries: 50,
maxAgeSeconds: 60 * 60 * 24, // 24小时
},
cacheableResponse: {
statuses: [0, 200],
},
},
},
// 静态资源使用缓存优先,但设置较短过期时间
{
urlPattern: /^https?.*\.(js|css|woff2?|png|jpg|jpeg|svg|gif|ico)/,
handler: 'StaleWhileRevalidate',
options: {
cacheName: 'static-resources',
expiration: {
maxEntries: 100,
maxAgeSeconds: 60 * 60 * 24 * 7, // 7天
},
},
},
],
},
}), }),
], ],
resolve: { resolve: {