# 性能優化指南

## 🚀 性能目標

| 指標 | 目標 | 工具 |
|------|------|------|
| **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: /(?<!node_modules.*)[\\/]node_modules[\\/](react|react-dom|scheduler)[\\/]/,
            priority: 40,
            enforce: true,
          },
          lib: {
            test(module) {
              return module.size() > 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: () => <Skeleton />,
    ssr: false, // 客戶端渲染
  }
)

// 條件載入
const AdminPanel = dynamic(
  () => import('@/components/AdminPanel'),
  {
    loading: () => <div>Loading admin panel...</div>,
  }
)

// 路由級別代碼分割
export default function Page() {
  const [showEditor, setShowEditor] = useState(false)

  return (
    <div>
      {showEditor && <FlashcardEditor />}
    </div>
  )
}
```

### 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 (
    <Image
      src={src}
      alt={alt}
      width={800}
      height={600}
      placeholder="blur" // 模糊預覽
      blurDataURL="data:image/jpeg;base64,..." // Base64 預覽圖
      loading="lazy" // 延遲載入
      quality={85} // 圖片品質
      sizes="(max-width: 768px) 100vw,
             (max-width: 1200px) 50vw,
             33vw"
    />
  )
}
```

### 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 (
    <div onClick={() => onSelect(card.id)}>
      {card.word}
    </div>
  )
}, (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 (
    <div>
      {sortedCards.map(card => (
        <FlashcardItem
          key={card.id}
          card={card}
          onSelect={handleSelect}
        />
      ))}
    </div>
  )
}
```

### 2. 虛擬滾動

```typescript
// components/VirtualList.tsx
import { FixedSizeList } from 'react-window'

export function VirtualFlashcardList({ cards }: { cards: Card[] }) {
  const Row = ({ index, style }: { index: number; style: any }) => (
    <div style={style}>
      <FlashcardItem card={cards[index]} />
    </div>
  )

  return (
    <FixedSizeList
      height={600} // 容器高度
      itemCount={cards.length}
      itemSize={80} // 每項高度
      width="100%"
    >
      {Row}
    </FixedSizeList>
  )
}
```

### 3. Suspense 與並行渲染

```typescript
// app/dashboard/page.tsx
import { Suspense } from 'react'

export default function Dashboard() {
  return (
    <div>
      <Suspense fallback={<StatsSkeletion />}>
        <UserStats /> {/* 異步組件 */}
      </Suspense>

      <Suspense fallback={<CardsSkeletion />}>
        <RecentFlashcards /> {/* 異步組件 */}
      </Suspense>

      <Suspense fallback={<ProgressSkeletion />}>
        <LearningProgress /> {/* 異步組件 */}
      </Suspense>
    </div>
  )
}
```

## 🗄️ 數據獲取優化

### 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 <FlashcardDetail flashcard={flashcard} />
}
```

### 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 (
    <Html>
      <Head>
        <style
          id="stitches"
          dangerouslySetInnerHTML={{ __html: getCssText() }}
        />
      </Head>
      <body>
        <Main />
        <NextScript />
      </body>
    </Html>
  )
}
```

### 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 (
    <html>
      <body>
        {children}
        <WebVitalsReporter />
      </body>
    </html>
  )
}

// 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