# 性能優化指南
## 🚀 性能目標
| 指標 | 目標 | 工具 |
|------|------|------|
| **First Contentful Paint (FCP)** | < 1.8s | Lighthouse |
| **Largest Contentful Paint (LCP)** | < 2.5s | Web Vitals |
| **First Input Delay (FID)** | < 100ms | Web Vitals |
| **Cumulative Layout Shift (CLS)** | < 0.1 | Web Vitals |
| **Time to Interactive (TTI)** | < 3.8s | Lighthouse |
| **Bundle Size** | < 200KB (gzipped) | Webpack Bundle Analyzer |
## 📦 打包優化
### 1. 代碼分割策略
```typescript
// next.config.js
module.exports = {
experimental: {
optimizeCss: true,
},
webpack: (config, { isServer }) => {
if (!isServer) {
config.optimization.splitChunks = {
chunks: 'all',
cacheGroups: {
default: false,
vendors: false,
framework: {
name: 'framework',
chunks: 'all',
test: /(? 160000 &&
/node_modules[/\\]/.test(module.identifier())
},
name(module) {
const hash = crypto.createHash('sha1')
hash.update(module.identifier())
return hash.digest('hex').substring(0, 8)
},
priority: 30,
minChunks: 1,
reuseExistingChunk: true,
},
commons: {
name: 'commons',
chunks: 'all',
minChunks: 2,
priority: 20,
},
shared: {
name(module, chunks) {
return crypto
.createHash('sha1')
.update(chunks.reduce((acc, chunk) => acc + chunk.name, ''))
.digest('hex')
},
priority: 10,
minChunks: 2,
reuseExistingChunk: true,
},
},
}
}
return config
},
}
```
### 2. 動態導入
```typescript
// 使用動態導入減少初始載入
import dynamic from 'next/dynamic'
// 延遲載入大型組件
const FlashcardEditor = dynamic(
() => import('@/components/FlashcardEditor'),
{
loading: () => ,
ssr: false, // 客戶端渲染
}
)
// 條件載入
const AdminPanel = dynamic(
() => import('@/components/AdminPanel'),
{
loading: () =>
Loading admin panel...
,
}
)
// 路由級別代碼分割
export default function Page() {
const [showEditor, setShowEditor] = useState(false)
return (
{showEditor && }
)
}
```
### 3. Tree Shaking
```typescript
// utils/index.ts
// ❌ 錯誤:導出所有
export * from './helpers'
// ✅ 正確:具名導出
export { formatDate, parseJSON } from './helpers'
// 使用時
// ❌ 錯誤:導入整個庫
import * as utils from '@/utils'
// ✅ 正確:只導入需要的
import { formatDate } from '@/utils'
```
## 🖼️ 圖片優化
### 1. Next.js Image 優化
```typescript
// components/OptimizedImage.tsx
import Image from 'next/image'
export function OptimizedImage({ src, alt }: { src: string; alt: string }) {
return (
)
}
```
### 2. 響應式圖片
```typescript
// utils/imageOptimization.ts
export function generateImageSizes(src: string) {
const sizes = [640, 750, 828, 1080, 1200, 1920, 2048, 3840]
return {
src,
srcSet: sizes
.map(size => `${src}?w=${size} ${size}w`)
.join(', '),
sizes: '(max-width: 640px) 100vw, (max-width: 1200px) 50vw, 33vw',
}
}
```
## ⚡ 渲染優化
### 1. React 組件優化
```typescript
// components/FlashcardList.tsx
import { memo, useMemo, useCallback } from 'react'
// 使用 memo 避免不必要的重新渲染
const FlashcardItem = memo(({ card, onSelect }: FlashcardItemProps) => {
return (
onSelect(card.id)}>
{card.word}
)
}, (prevProps, nextProps) => {
// 自定義比較函數
return prevProps.card.id === nextProps.card.id &&
prevProps.card.word === nextProps.card.word
})
export function FlashcardList({ cards }: { cards: Card[] }) {
// 使用 useCallback 避免函數重新創建
const handleSelect = useCallback((id: string) => {
console.log('Selected:', id)
}, [])
// 使用 useMemo 快取計算結果
const sortedCards = useMemo(() => {
return [...cards].sort((a, b) => a.word.localeCompare(b.word))
}, [cards])
return (
{sortedCards.map(card => (
))}
)
}
```
### 2. 虛擬滾動
```typescript
// components/VirtualList.tsx
import { FixedSizeList } from 'react-window'
export function VirtualFlashcardList({ cards }: { cards: Card[] }) {
const Row = ({ index, style }: { index: number; style: any }) => (
)
return (
{Row}
)
}
```
### 3. Suspense 與並行渲染
```typescript
// app/dashboard/page.tsx
import { Suspense } from 'react'
export default function Dashboard() {
return (
}>
{/* 異步組件 */}
}>
{/* 異步組件 */}
}>
{/* 異步組件 */}
)
}
```
## 🗄️ 數據獲取優化
### 1. 數據預載入
```typescript
// app/flashcards/[id]/page.tsx
import { preloadFlashcard } from '@/lib/api/flashcards'
export default async function FlashcardPage({ params }: { params: { id: string } }) {
// 預載入相關數據
preloadFlashcard(params.id)
preloadRelatedFlashcards(params.id)
const flashcard = await getFlashcard(params.id)
return
}
```
### 2. 並行數據獲取
```typescript
// hooks/useParallelFetch.ts
export function useDashboardData() {
const [stats, flashcards, progress] = useQueries({
queries: [
{
queryKey: ['stats'],
queryFn: fetchUserStats,
staleTime: 5 * 60 * 1000, // 5 分鐘
},
{
queryKey: ['recent-flashcards'],
queryFn: fetchRecentFlashcards,
staleTime: 60 * 1000, // 1 分鐘
},
{
queryKey: ['progress'],
queryFn: fetchLearningProgress,
staleTime: 10 * 60 * 1000, // 10 分鐘
},
],
})
return {
stats: stats.data,
flashcards: flashcards.data,
progress: progress.data,
isLoading: stats.isLoading || flashcards.isLoading || progress.isLoading,
}
}
```
### 3. 無限滾動優化
```typescript
// hooks/useInfiniteFlashcards.ts
export function useInfiniteFlashcards() {
const {
data,
fetchNextPage,
hasNextPage,
isFetchingNextPage,
} = useInfiniteQuery({
queryKey: ['flashcards'],
queryFn: ({ pageParam = 0 }) => fetchFlashcards({ page: pageParam, limit: 20 }),
getNextPageParam: (lastPage, pages) => lastPage.nextCursor,
staleTime: 5 * 60 * 1000,
gcTime: 10 * 60 * 1000,
})
const allFlashcards = useMemo(
() => data?.pages.flatMap(page => page.items) ?? [],
[data]
)
return {
flashcards: allFlashcards,
loadMore: fetchNextPage,
hasMore: hasNextPage,
isLoading: isFetchingNextPage,
}
}
```
## 🎨 CSS 優化
### 1. Critical CSS
```typescript
// pages/_document.tsx
import { getCssText } from '@/stitches.config'
export default function Document() {
return (
)
}
```
### 2. CSS-in-JS 優化
```typescript
// 使用 CSS Variables 減少運行時計算
const theme = {
colors: {
primary: 'var(--color-primary)',
secondary: 'var(--color-secondary)',
},
}
// 避免動態樣式
// ❌ 錯誤
const Button = styled.button`
background: ${props => props.primary ? 'blue' : 'gray'};
`
// ✅ 正確
const Button = styled.button`
&[data-variant="primary"] {
background: var(--color-primary);
}
&[data-variant="secondary"] {
background: var(--color-secondary);
}
`
```
## 📊 監控與分析
### 1. Web Vitals 監控
```typescript
// app/layout.tsx
import { WebVitalsReporter } from '@/components/WebVitalsReporter'
export default function RootLayout({ children }) {
return (
{children}
)
}
// components/WebVitalsReporter.tsx
'use client'
import { useReportWebVitals } from 'next/web-vitals'
export function WebVitalsReporter() {
useReportWebVitals((metric) => {
// 發送到分析服務
if (metric.label === 'web-vital') {
console.log(metric)
// 發送到 Google Analytics
if (typeof window !== 'undefined' && window.gtag) {
window.gtag('event', metric.name, {
value: Math.round(metric.value),
event_label: metric.id,
non_interaction: true,
})
}
}
})
return null
}
```
### 2. Bundle 分析
```json
// package.json
{
"scripts": {
"analyze": "ANALYZE=true next build",
"analyze:server": "BUNDLE_ANALYZE=server next build",
"analyze:browser": "BUNDLE_ANALYZE=browser next build"
}
}
```
```typescript
// next.config.js
const withBundleAnalyzer = require('@next/bundle-analyzer')({
enabled: process.env.ANALYZE === 'true',
})
module.exports = withBundleAnalyzer({
// 其他配置
})
```
## 🔧 性能優化檢查清單
### 開發階段
- [ ] 使用 React DevTools Profiler 分析渲染
- [ ] 檢查不必要的重新渲染
- [ ] 實施代碼分割
- [ ] 優化圖片載入
- [ ] 使用適當的快取策略
### 構建優化
- [ ] 啟用生產模式構建
- [ ] 壓縮 JavaScript 和 CSS
- [ ] 移除未使用的代碼
- [ ] 優化字體載入
- [ ] 啟用 Brotli/Gzip 壓縮
### 運行時優化
- [ ] 實施延遲載入
- [ ] 使用 Service Worker 快取
- [ ] 優化第三方腳本載入
- [ ] 減少主線程工作
- [ ] 優化數據庫查詢
### 監控
- [ ] 設置 Real User Monitoring (RUM)
- [ ] 追蹤 Core Web Vitals
- [ ] 設置性能預算
- [ ] 定期進行 Lighthouse 審計
- [ ] 監控 JavaScript 錯誤率
## 📈 性能預算
```javascript
// performance-budget.js
module.exports = {
bundles: [
{
name: 'main',
maxSize: '150kb',
},
{
name: 'vendor',
maxSize: '250kb',
},
],
metrics: {
fcp: 1800,
lcp: 2500,
fid: 100,
cls: 0.1,
tti: 3800,
},
}
```
## 🚀 快速優化清單
### 立即可做
1. 啟用 Next.js Image 組件
2. 添加 loading="lazy" 到圖片
3. 預連接到外部域名
4. 內聯關鍵 CSS
5. 延遲非關鍵 JavaScript
### 短期改進
1. 實施虛擬滾動
2. 優化字體載入策略
3. 使用 Web Workers
4. 實施預載入策略
5. 優化動畫性能
### 長期優化
1. 實施 Edge Functions
2. 使用 ISR (增量靜態再生)
3. 優化資料庫索引
4. 實施 CDN 策略
5. 考慮使用 WebAssembly