Compare commits
	
		
			20 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 006f3d4dbb | ||
|   | ff8d497f97 | ||
|   | 9c2799f7d5 | ||
|   | dd70f9d886 | ||
|   | 35cccf6a8f | ||
| c5616600fa | |||
|   | 986708fbb4 | ||
|   | 6a1b68ed2c | ||
|   | 32970acf32 | ||
|   | 812bb8c248 | ||
|   | 09f9e6588f | ||
|   | 10a167febd | ||
|   | 91c0686a46 | ||
|   | 40bfde8e57 | ||
|   | b4ba7a2219 | ||
|   | 25e42e3af5 | ||
|   | 4398b53ea7 | ||
|   | 3e14bc652f | ||
|   | aca7e11835 | ||
|   | 782de6e38a | 
							
								
								
									
										171
									
								
								SEO-README.md
									
									
									
									
									
								
							
							
						
						
									
										171
									
								
								SEO-README.md
									
									
									
									
									
								
							| @@ -1,171 +0,0 @@ | |||||||
| # SEO 优化说明 |  | ||||||
|  |  | ||||||
| 本项目已完成基础 SEO 优化,以下是已实施的改动。 |  | ||||||
|  |  | ||||||
| ## 已完成的优化项 |  | ||||||
|  |  | ||||||
| ### 1. HTML Meta 标签优化 ✅ |  | ||||||
|  |  | ||||||
| 在 `index.html` 中添加了完整的 SEO 元数据: |  | ||||||
|  |  | ||||||
| - **基础 Meta 标签** |  | ||||||
|   - title, description, keywords |  | ||||||
|   - author, theme-color |  | ||||||
|   - canonical URL |  | ||||||
|    |  | ||||||
| - **Open Graph 标签**(社交媒体分享优化) |  | ||||||
|   - og:type, og:url, og:title |  | ||||||
|   - og:description, og:image, og:site_name |  | ||||||
|    |  | ||||||
| - **Twitter Card 标签** |  | ||||||
|   - twitter:card, twitter:title |  | ||||||
|   - twitter:description, twitter:image |  | ||||||
|  |  | ||||||
| - **结构化数据**(JSON-LD) |  | ||||||
|   - Schema.org WebSite 类型标记 |  | ||||||
|   - 提升搜索引擎理解能力 |  | ||||||
|  |  | ||||||
| ### 2. SEO 配置文件 ✅ |  | ||||||
|  |  | ||||||
| - **`public/robots.txt`** - 搜索引擎爬虫规则 |  | ||||||
|   - 允许所有爬虫访问 |  | ||||||
|   - 指定 sitemap 位置 |  | ||||||
|    |  | ||||||
| - **`public/sitemap.xml`** - 站点地图(自动生成) |  | ||||||
|   - 包含所有工具页面 URL |  | ||||||
|   - 设置更新频率和优先级 |  | ||||||
|   - 通过 `scripts/generate-sitemap.ts` 自动生成 |  | ||||||
|    |  | ||||||
| - **`public/manifest.json`** - PWA 配置 |  | ||||||
|   - 支持渐进式 Web 应用 |  | ||||||
|   - 改善移动端体验 |  | ||||||
|  |  | ||||||
| ### 3. 构建流程优化 ✅ |  | ||||||
|  |  | ||||||
| - **`package.json`** |  | ||||||
|   - 构建时自动生成 sitemap |  | ||||||
|   - 添加 tsx 依赖用于运行生成脚本 |  | ||||||
|  |  | ||||||
| ## 文件清单 |  | ||||||
|  |  | ||||||
| ### 新增文件 |  | ||||||
| ``` |  | ||||||
| public/ |  | ||||||
| ├── robots.txt              # 爬虫规则 |  | ||||||
| ├── sitemap.xml            # 站点地图(自动生成) |  | ||||||
| └── manifest.json          # PWA 配置 |  | ||||||
|  |  | ||||||
| scripts/ |  | ||||||
| └── generate-sitemap.ts    # Sitemap 生成脚本 |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| ### 修改文件 |  | ||||||
| ``` |  | ||||||
| index.html                 # 添加 SEO meta 标签和结构化数据 |  | ||||||
| package.json               # 添加 sitemap 生成命令 |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| ## 使用说明 |  | ||||||
|  |  | ||||||
| ### 开发 |  | ||||||
| ```bash |  | ||||||
| pnpm install               # 安装依赖 |  | ||||||
| pnpm dev                   # 启动开发服务器 |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| ### 构建 |  | ||||||
| ```bash |  | ||||||
| pnpm run build             # 构建项目(自动生成 sitemap) |  | ||||||
| pnpm run generate:sitemap  # 单独生成 sitemap |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| ### 部署前检查 |  | ||||||
|  |  | ||||||
| ⚠️ **重要:部署前必须更新配置文件中的域名!** |  | ||||||
|  |  | ||||||
| 需要将以下文件中的 `https://litek.typist.cc` 替换为你的实际域名: |  | ||||||
|  |  | ||||||
| 1. **`index.html`** |  | ||||||
|    - canonical URL |  | ||||||
|    - Open Graph URL 和 image |  | ||||||
|    - Twitter Card image |  | ||||||
|    - Structured Data URL |  | ||||||
|  |  | ||||||
| 2. **`public/robots.txt`** |  | ||||||
|    - Sitemap URL |  | ||||||
|  |  | ||||||
| 3. **`scripts/generate-sitemap.ts`** |  | ||||||
|    - BASE_URL 常量 |  | ||||||
|  |  | ||||||
| 更新后重新构建: |  | ||||||
| ```bash |  | ||||||
| pnpm run build |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| ## SEO 验证 |  | ||||||
|  |  | ||||||
| ### 部署后验证 |  | ||||||
|  |  | ||||||
| 1. **提交 sitemap 到 Google Search Console** |  | ||||||
|    - 访问 https://search.google.com/search-console |  | ||||||
|    - 添加网站资源 |  | ||||||
|    - 提交 sitemap: `https://你的域名/sitemap.xml` |  | ||||||
|  |  | ||||||
| 2. **测试结构化数据** |  | ||||||
|    - 访问 https://validator.schema.org/ |  | ||||||
|    - 输入网站 URL |  | ||||||
|    - 检查是否有错误 |  | ||||||
|  |  | ||||||
| 3. **测试 Open Graph 预览** |  | ||||||
|    - Facebook: https://developers.facebook.com/tools/debug/ |  | ||||||
|    - Twitter: https://cards-dev.twitter.com/validator |  | ||||||
|  |  | ||||||
| 4. **性能测试** |  | ||||||
|    - Google PageSpeed Insights: https://pagespeed.web.dev/ |  | ||||||
|    - 目标分数: SEO > 95 |  | ||||||
|  |  | ||||||
| ## 可访问的 URL |  | ||||||
|  |  | ||||||
| 部署后,以下 URL 应该可以正常访问: |  | ||||||
|  |  | ||||||
| - `https://你的域名/` - 主页 |  | ||||||
| - `https://你的域名/robots.txt` - 爬虫规则 |  | ||||||
| - `https://你的域名/sitemap.xml` - 站点地图 |  | ||||||
| - `https://你的域名/manifest.json` - PWA 配置 |  | ||||||
| - `https://你的域名/tool/uuid` - UUID 工具 |  | ||||||
| - `https://你的域名/tool/json` - JSON 工具 |  | ||||||
| - `https://你的域名/tool/base64` - Base64 工具 |  | ||||||
| - `https://你的域名/tool/network/dns` - DNS 工具 |  | ||||||
| - ... 其他工具页面 |  | ||||||
|  |  | ||||||
| ## 添加新工具时更新 SEO |  | ||||||
|  |  | ||||||
| 当添加新工具时,需要更新 `scripts/generate-sitemap.ts`: |  | ||||||
|  |  | ||||||
| ```typescript |  | ||||||
| const tools = [ |  | ||||||
|   // 现有工具... |  | ||||||
|   { path: '/tool/你的新工具', priority: '0.9' }, |  | ||||||
| ]; |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| 然后重新构建项目。 |  | ||||||
|  |  | ||||||
| ## SEO 效果预期 |  | ||||||
|  |  | ||||||
| - **1-2 周**:搜索引擎开始索引页面 |  | ||||||
| - **1-2 月**:主要关键词开始有排名 |  | ||||||
| - **3-6 月**:稳定的搜索排名和自然流量增长 |  | ||||||
|  |  | ||||||
| ## 技术栈 |  | ||||||
|  |  | ||||||
| - React 19 + TypeScript |  | ||||||
| - Vite (Rolldown) |  | ||||||
| - React Router v7 |  | ||||||
| - Radix UI + Tailwind CSS 4 |  | ||||||
| - Nginx |  | ||||||
|  |  | ||||||
| ## 联系方式 |  | ||||||
|  |  | ||||||
| 需要更多工具或有建议?联系:litek@mail.typist.cc |  | ||||||
|  |  | ||||||
| @@ -18,6 +18,11 @@ | |||||||
|     <link rel="dns-prefetch" href="https://ipinfo.io"> |     <link rel="dns-prefetch" href="https://ipinfo.io"> | ||||||
|     <link rel="preconnect" href="https://ipinfo.io" crossorigin> |     <link rel="preconnect" href="https://ipinfo.io" crossorigin> | ||||||
|      |      | ||||||
|  |     <!-- Google Fonts --> | ||||||
|  |     <link rel="preconnect" href="https://fonts.googleapis.com"> | ||||||
|  |     <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> | ||||||
|  |     <link href="https://fonts.googleapis.com/css2?family=Roboto+Mono:wght@400;500&family=Noto+Sans+SC:wght@400;500&display=swap" rel="stylesheet"> | ||||||
|  |      | ||||||
|     <!-- Open Graph / Facebook --> |     <!-- Open Graph / Facebook --> | ||||||
|     <meta property="og:type" content="website" /> |     <meta property="og:type" content="website" /> | ||||||
|     <meta property="og:url" content="https://litek.typist.cc/" /> |     <meta property="og:url" content="https://litek.typist.cc/" /> | ||||||
| @@ -59,5 +64,9 @@ | |||||||
|   <body> |   <body> | ||||||
|     <div id="root"></div> |     <div id="root"></div> | ||||||
|     <script type="module" src="/src/main.tsx"></script> |     <script type="module" src="/src/main.tsx"></script> | ||||||
|  |  | ||||||
|  |     <!-- Cloudflare Web Analytics --> | ||||||
|  |     <script defer src='https://static.cloudflareinsights.com/beacon.min.js' data-cf-beacon='{"token": "2aecdc025eb043bc89ce931b54a80054"}'></script> | ||||||
|  |     <!-- End Cloudflare Web Analytics --> | ||||||
|   </body> |   </body> | ||||||
| </html> | </html> | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| { | { | ||||||
|   "name": "litek", |   "name": "litek", | ||||||
|   "private": true, |   "private": true, | ||||||
|   "version": "0.0.19", |   "version": "0.0.28", | ||||||
|   "type": "module", |   "type": "module", | ||||||
|   "scripts": { |   "scripts": { | ||||||
|     "dev": "vite", |     "dev": "vite", | ||||||
| @@ -44,11 +44,14 @@ | |||||||
|     "eslint-plugin-react-hooks": "^5.2.0", |     "eslint-plugin-react-hooks": "^5.2.0", | ||||||
|     "eslint-plugin-react-refresh": "^0.4.22", |     "eslint-plugin-react-refresh": "^0.4.22", | ||||||
|     "globals": "^16.4.0", |     "globals": "^16.4.0", | ||||||
|  |     "terser": "^5.44.0", | ||||||
|     "tsx": "^4.19.2", |     "tsx": "^4.19.2", | ||||||
|     "tw-animate-css": "^1.4.0", |     "tw-animate-css": "^1.4.0", | ||||||
|     "typescript": "~5.9.3", |     "typescript": "~5.9.3", | ||||||
|     "typescript-eslint": "^8.45.0", |     "typescript-eslint": "^8.45.0", | ||||||
|     "vite": "npm:rolldown-vite@7.1.14" |     "vite": "npm:rolldown-vite@7.1.14", | ||||||
|  |     "vite-plugin-pwa": "^1.1.0", | ||||||
|  |     "workbox-window": "^7.3.0" | ||||||
|   }, |   }, | ||||||
|   "pnpm": { |   "pnpm": { | ||||||
|     "overrides": { |     "overrides": { | ||||||
|   | |||||||
							
								
								
									
										2811
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										2811
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -114,11 +114,7 @@ export const AppSidebar = () => { | |||||||
|           </SidebarGroupContent> |           </SidebarGroupContent> | ||||||
|         </SidebarGroup> |         </SidebarGroup> | ||||||
|       </SidebarContent> |       </SidebarContent> | ||||||
|       <SidebarFooter className="flex flex-row justify-between items-center gap-2"> |       <SidebarFooter className="flex flex-col gap-2" /> | ||||||
|         <SidebarMenuButton variant="outline" asChild> |  | ||||||
|           <a href="mailto:litek@mail.typist.cc">need more tools?</a> |  | ||||||
|         </SidebarMenuButton> |  | ||||||
|       </SidebarFooter> |  | ||||||
|     </Sidebar> |     </Sidebar> | ||||||
|   ); |   ); | ||||||
| }; | }; | ||||||
| @@ -1,22 +1,94 @@ | |||||||
| import { type FC } from "react"; | import { type FC, useState } from "react"; | ||||||
|  | import { RefreshCw, Copy } from "lucide-react"; | ||||||
| import * as uuid from 'uuid' | import * as uuid from 'uuid' | ||||||
| import { nanoid } from 'nanoid' | import { nanoid } from 'nanoid' | ||||||
|  | import { Button } from "@/components/ui/button"; | ||||||
|  | import { toast } from "sonner"; | ||||||
|  |  | ||||||
|  | interface IDGeneratorProps { | ||||||
|  |   label: string; | ||||||
|  |   value: string; | ||||||
|  |   onRegenerate: () => void; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | const IDGenerator: FC<IDGeneratorProps> = ({ label, value, onRegenerate }) => { | ||||||
|  |   const copyToClipboard = async () => { | ||||||
|  |     try { | ||||||
|  |       await navigator.clipboard.writeText(value); | ||||||
|  |       toast(`${label} has been copied to clipboard`); | ||||||
|  |     } catch (err) { | ||||||
|  |       toast.error("Copy failed"); | ||||||
|  |     } | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   return ( | ||||||
|  |     <div className="flex flex-col gap-2"> | ||||||
|  |       <label className="font-medium">{label}</label> | ||||||
|  |       <div className="flex items-center gap-2"> | ||||||
|  |         <span className="flex-1 px-3 py-2 bg-muted rounded-md font-mono text-sm break-all max-w-[400px]"> | ||||||
|  |           {value} | ||||||
|  |         </span> | ||||||
|  |         <Button | ||||||
|  |           size="icon" | ||||||
|  |           variant="outline" | ||||||
|  |           onClick={onRegenerate} | ||||||
|  |           title="Regenerate" | ||||||
|  |         > | ||||||
|  |           <RefreshCw className="h-4 w-4" /> | ||||||
|  |         </Button> | ||||||
|  |         <Button | ||||||
|  |           size="icon" | ||||||
|  |           variant="outline" | ||||||
|  |           onClick={copyToClipboard} | ||||||
|  |           title="Copy" | ||||||
|  |         > | ||||||
|  |           <Copy className="h-4 w-4" /> | ||||||
|  |         </Button> | ||||||
|  |       </div> | ||||||
|  |     </div> | ||||||
|  |   ); | ||||||
|  | }; | ||||||
|  |  | ||||||
| const Tool: FC = () => { | const Tool: FC = () => { | ||||||
|  |   const [uuidV1, setUuidV1] = useState(() => uuid.v1()); | ||||||
|  |   const [uuidV4, setUuidV4] = useState(() => uuid.v4()); | ||||||
|  |   const [uuidV6, setUuidV6] = useState(() => uuid.v6()); | ||||||
|  |   const [uuidV7, setUuidV7] = useState(() => uuid.v7()); | ||||||
|  |   const [nanoId, setNanoId] = useState(() => nanoid()); | ||||||
|  |  | ||||||
|   return ( |   return ( | ||||||
|     <div className="flex flex-col gap-4"> |     <div className="flex flex-col gap-4"> | ||||||
|       <span className="text-sm text-muted-foreground">Refresh the page to generate new UUID</span> |       <span className="text-sm text-muted-foreground">Click the refresh button to regenerate the corresponding ID</span> | ||||||
|       <label>UUID Version 1</label> |        | ||||||
|       <span>{uuid.v1()}</span> |       <IDGenerator  | ||||||
|       <label>UUID Version 4</label> |         label="UUID Version 1"  | ||||||
|       <span>{uuid.v4()}</span> |         value={uuidV1}  | ||||||
|       <label>UUID Version 6</label> |         onRegenerate={() => setUuidV1(uuid.v1())}  | ||||||
|       <span>{uuid.v6()}</span> |       /> | ||||||
|       <label>UUID Version 7</label> |        | ||||||
|       <span>{uuid.v7()}</span> |       <IDGenerator  | ||||||
|       <label>Nano ID</label> |         label="UUID Version 4"  | ||||||
|       <span>{nanoid()}</span> |         value={uuidV4}  | ||||||
|  |         onRegenerate={() => setUuidV4(uuid.v4())}  | ||||||
|  |       /> | ||||||
|  |        | ||||||
|  |       <IDGenerator  | ||||||
|  |         label="UUID Version 6"  | ||||||
|  |         value={uuidV6}  | ||||||
|  |         onRegenerate={() => setUuidV6(uuid.v6())}  | ||||||
|  |       /> | ||||||
|  |        | ||||||
|  |       <IDGenerator  | ||||||
|  |         label="UUID Version 7"  | ||||||
|  |         value={uuidV7}  | ||||||
|  |         onRegenerate={() => setUuidV7(uuid.v7())}  | ||||||
|  |       /> | ||||||
|  |        | ||||||
|  |       <IDGenerator  | ||||||
|  |         label="Nano ID"  | ||||||
|  |         value={nanoId}  | ||||||
|  |         onRegenerate={() => setNanoId(nanoid())}  | ||||||
|  |       /> | ||||||
|     </div> |     </div> | ||||||
|   ); |   ); | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -116,6 +116,7 @@ | |||||||
|   } |   } | ||||||
|   body { |   body { | ||||||
|     @apply bg-background text-foreground; |     @apply bg-background text-foreground; | ||||||
|  |     font-family: 'Roboto Mono', 'Noto Sans SC', 'SF Mono', Consolas, monospace; | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										18
									
								
								src/main.tsx
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								src/main.tsx
									
									
									
									
									
								
							| @@ -13,3 +13,21 @@ createRoot(document.getElementById('root')!).render( | |||||||
|     <Toaster /> |     <Toaster /> | ||||||
|   </StrictMode> |   </StrictMode> | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | // 注册 Service Worker | ||||||
|  | if ('serviceWorker' in navigator && import.meta.env.PROD) { | ||||||
|  |   import('workbox-window').then(({ Workbox }) => { | ||||||
|  |     const wb = new Workbox('/sw.js') | ||||||
|  |      | ||||||
|  |     wb.addEventListener('installed', (event) => { | ||||||
|  |       if (event.isUpdate) { | ||||||
|  |         console.log('New service worker installed, reloading page...') | ||||||
|  |         window.location.reload() | ||||||
|  |       } | ||||||
|  |     }) | ||||||
|  |  | ||||||
|  |     wb.register() | ||||||
|  |   }).catch((error) => { | ||||||
|  |     console.error('Failed to register service worker:', error) | ||||||
|  |   }) | ||||||
|  | } | ||||||
|   | |||||||
							
								
								
									
										3
									
								
								src/vite-env.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								src/vite-env.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | |||||||
|  | /// <reference types="vite/client" /> | ||||||
|  | /// <reference types="vite-plugin-pwa/client" /> | ||||||
|  |  | ||||||
							
								
								
									
										108
									
								
								vite.config.ts
									
									
									
									
									
								
							
							
						
						
									
										108
									
								
								vite.config.ts
									
									
									
									
									
								
							| @@ -2,10 +2,85 @@ import path from "path" | |||||||
| import { defineConfig } from 'vite' | import { defineConfig } from 'vite' | ||||||
| import react from '@vitejs/plugin-react' | import react from '@vitejs/plugin-react' | ||||||
| import tailwindcss from "@tailwindcss/vite" | import tailwindcss from "@tailwindcss/vite" | ||||||
|  | import { VitePWA } from 'vite-plugin-pwa' | ||||||
|  |  | ||||||
| // https://vite.dev/config/ | // https://vite.dev/config/ | ||||||
| export default defineConfig({ | export default defineConfig({ | ||||||
|   plugins: [react(), tailwindcss()], |   plugins: [ | ||||||
|  |     react(),  | ||||||
|  |     tailwindcss(), | ||||||
|  |     VitePWA({ | ||||||
|  |       registerType: 'autoUpdate', | ||||||
|  |       includeAssets: ['lite.svg', 'robots.txt', 'sitemap.xml'], | ||||||
|  |       manifest: { | ||||||
|  |         name: 'Lite Kit - Lightweight Online Tools', | ||||||
|  |         short_name: 'Lite Kit', | ||||||
|  |         description: 'Free online tools including UUID generator, JSON formatter, Base64 encoder/decoder, network testing tools and more', | ||||||
|  |         theme_color: '#000000', | ||||||
|  |         background_color: '#000000', | ||||||
|  |         display: 'standalone', | ||||||
|  |         icons: [ | ||||||
|  |           { | ||||||
|  |             src: '/lite.svg', | ||||||
|  |             type: 'image/svg+xml', | ||||||
|  |             sizes: 'any' | ||||||
|  |           } | ||||||
|  |         ] | ||||||
|  |       }, | ||||||
|  |       workbox: { | ||||||
|  |         globPatterns: ['**/*.{js,css,html,svg,png,ico,woff2}'], | ||||||
|  |         runtimeCaching: [ | ||||||
|  |           { | ||||||
|  |             urlPattern: /^https:\/\/ipinfo\.io\/.*/i, | ||||||
|  |             handler: "NetworkFirst", | ||||||
|  |             options: { | ||||||
|  |               cacheName: 'ipinfo-cache', | ||||||
|  |               expiration: { | ||||||
|  |                 maxEntries: 10, | ||||||
|  |                 maxAgeSeconds: 60 * 60 // 延长到 1 小时 | ||||||
|  |               }, | ||||||
|  |               cacheableResponse: { | ||||||
|  |                 statuses: [0, 200] | ||||||
|  |               } | ||||||
|  |             } | ||||||
|  |           }, | ||||||
|  |           { | ||||||
|  |             // Google Fonts 样式表缓存 | ||||||
|  |             urlPattern: /^https:\/\/fonts\.googleapis\.com\/.*/i, | ||||||
|  |             handler: 'StaleWhileRevalidate', | ||||||
|  |             options: { | ||||||
|  |               cacheName: 'google-fonts-stylesheets', | ||||||
|  |               expiration: { | ||||||
|  |                 maxEntries: 10, | ||||||
|  |                 maxAgeSeconds: 60 * 60 * 24 * 365 // 1 年 | ||||||
|  |               }, | ||||||
|  |               cacheableResponse: { | ||||||
|  |                 statuses: [0, 200] | ||||||
|  |               } | ||||||
|  |             } | ||||||
|  |           }, | ||||||
|  |           { | ||||||
|  |             // Google Fonts 字体文件缓存 | ||||||
|  |             urlPattern: /^https:\/\/fonts\.gstatic\.com\/.*/i, | ||||||
|  |             handler: 'CacheFirst', | ||||||
|  |             options: { | ||||||
|  |               cacheName: 'google-fonts-webfonts', | ||||||
|  |               expiration: { | ||||||
|  |                 maxEntries: 30, | ||||||
|  |                 maxAgeSeconds: 60 * 60 * 24 * 365 // 1 年 | ||||||
|  |               }, | ||||||
|  |               cacheableResponse: { | ||||||
|  |                 statuses: [0, 200] | ||||||
|  |               } | ||||||
|  |             } | ||||||
|  |           } | ||||||
|  |         ], | ||||||
|  |         cleanupOutdatedCaches: true, | ||||||
|  |         clientsClaim: true, | ||||||
|  |         skipWaiting: true | ||||||
|  |       } | ||||||
|  |     }) | ||||||
|  |   ], | ||||||
|   resolve: { |   resolve: { | ||||||
|     alias: { |     alias: { | ||||||
|       "@": path.resolve(__dirname, "./src"), |       "@": path.resolve(__dirname, "./src"), | ||||||
| @@ -15,11 +90,15 @@ export default defineConfig({ | |||||||
|     rollupOptions: { |     rollupOptions: { | ||||||
|       output: { |       output: { | ||||||
|         manualChunks: (id) => { |         manualChunks: (id) => { | ||||||
|           // React核心库 |           // React 核心拆分得更细 | ||||||
|           if (id.includes('node_modules/react') ||  |           if (id.includes('node_modules/react/') && !id.includes('node_modules/react-dom')) { | ||||||
|               id.includes('node_modules/react-dom') ||  |             return 'react-core'; | ||||||
|               id.includes('node_modules/react-router-dom')) { |           } | ||||||
|             return 'react-vendor'; |           if (id.includes('node_modules/react-dom/')) { | ||||||
|  |             return 'react-dom'; | ||||||
|  |           } | ||||||
|  |           if (id.includes('node_modules/react-router-dom')) { | ||||||
|  |             return 'react-router'; | ||||||
|           } |           } | ||||||
|           // Radix UI组件 |           // Radix UI组件 | ||||||
|           if (id.includes('node_modules/@radix-ui')) { |           if (id.includes('node_modules/@radix-ui')) { | ||||||
| @@ -29,9 +108,26 @@ export default defineConfig({ | |||||||
|           if (id.includes('node_modules/lucide-react')) { |           if (id.includes('node_modules/lucide-react')) { | ||||||
|             return 'icons'; |             return 'icons'; | ||||||
|           } |           } | ||||||
|  |           // 其他工具库 | ||||||
|  |           if (id.includes('node_modules/')) { | ||||||
|  |             return 'vendor'; | ||||||
|  |           } | ||||||
|         }, |         }, | ||||||
|       }, |       }, | ||||||
|     }, |     }, | ||||||
|  |     // 启用更激进的压缩 | ||||||
|  |     minify: 'terser', | ||||||
|  |     terserOptions: { | ||||||
|  |       compress: { | ||||||
|  |         drop_console: true, | ||||||
|  |         drop_debugger: true, | ||||||
|  |         pure_funcs: ['console.log'], | ||||||
|  |         // 移除未使用的代码 | ||||||
|  |         unused: true, | ||||||
|  |         // 移除死代码 | ||||||
|  |         dead_code: true, | ||||||
|  |       }, | ||||||
|  |     }, | ||||||
|     chunkSizeWarningLimit: 500, |     chunkSizeWarningLimit: 500, | ||||||
|   }, |   }, | ||||||
| }) | }) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user