refactor: Store 架構重構 - 按功能模組組織

重構內容:
• 創建 store/review/ 資料夾,集中管理複習相關 Store
• 移動 5 個 Store 文件到 review 模組下
• 重新命名 useUIStore → useReviewUIStore,語義更明確
• 更新所有 import 路徑,保持一致性

架構改善:
• Store 按功能模組組織,而非按類型組織
• 語義更明確:一看就知道是 Review 功能相關
• 為未來功能模組擴展奠定基礎
• 更新 README 文檔反映新架構

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
鄭沛軒 2025-10-02 16:01:58 +08:00
parent df1c2b92ef
commit 97704a7dfa
15 changed files with 517 additions and 27 deletions

View File

@ -0,0 +1,488 @@
# DramaLing 前端系統架構分析報告
## 📋 專案概覽
**技術棧**
- **框架**Next.js 15.5.3 (App Router)
- **語言**TypeScript 5.9.2
- **狀態管理**Zustand 5.0.8
- **樣式**Tailwind CSS 3.4.17
- **圖標**Lucide React
- **部署**Zeabur
**專案規模**
- 約 150+ 個組件和模組文件
- 8 個主要路由頁面
- 5 個 Zustand Store
- 多層級的服務和工具函數架構
## 🏗️ 系統架構層次
### 1. 目錄結構分析
```
frontend/
├── app/ # Next.js App Router 頁面
│ ├── flashcards/ # 詞卡管理頁面
│ ├── generate/ # AI 生成頁面
│ ├── review/ # 複習系統頁面
│ ├── review-design/ # 複習設計頁面
│ └── settings/ # 設定頁面
├── components/ # React 組件庫
│ ├── shared/ # 共享基礎組件 (12個)
│ ├── flashcards/ # 詞卡功能組件 (10個)
│ ├── review/ # 複習系統組件 (20+個)
│ ├── generate/ # AI生成組件 (5個)
│ ├── ui/ # 基礎UI組件 (1個)
│ ├── word/ # 詞彙分析組件 (3個)
│ └── media/ # 媒體組件 (已重構)
├── hooks/ # 自定義 Hook 層
│ ├── flashcards/ # 詞卡邏輯 (7個)
│ ├── review/ # 複習邏輯 (2個)
│ ├── generate/ # 生成邏輯 (2個)
│ └── shared/ # 共享邏輯 (1個)
├── store/ # Zustand 狀態管理
│ ├── useReviewSessionStore.ts # 複習會話
│ ├── useTestQueueStore.ts # 測試佇列
│ ├── useReviewDataStore.ts # 複習數據
│ ├── useTestResultStore.ts # 測試結果
│ └── useUIStore.ts # UI狀態
├── lib/ # 核心服務層
│ ├── services/ # API 服務
│ ├── utils/ # 工具函數
│ ├── types/ # 類型定義
│ └── config/ # 配置管理
└── types/ # 全域類型定義
```
## 🎯 架構設計模式分析
### 2.1 分層架構模式 (Layered Architecture)
```
┌─────────────────┐
│ 表現層 (UI) │ ← React 組件
├─────────────────┤
│ 業務邏輯層 │ ← Custom Hooks
├─────────────────┤
│ 狀態管理層 │ ← Zustand Stores
├─────────────────┤
│ 服務層 (API) │ ← API Services
└─────────────────┘
```
**設計優勢**
- ✅ 職責分離清晰
- ✅ 層次間耦合度低
- ✅ 易於測試和維護
- ✅ 支援並行開發
### 2.2 Hooks + Store 雙層模式
**Hook 層**(業務邏輯封裝):
- 包裝 Store 操作 + API 呼叫
- 提供組件友好的介面
- 處理複雜的業務邏輯
**Store 層**(純狀態管理):
- Zustand 全域狀態
- 跨組件狀態同步
- 最小重渲染優化
## 📊 組件架構分析
### 3.1 共享組件庫 (components/shared/)
**基礎組件** (12個)
- `BluePlayButton` - 統一播放按鈕 🆕
- `ContentBlock` - 內容區塊
- `ErrorState` - 錯誤狀態顯示
- `LoadingState` - 載入狀態
- `Modal` - 彈窗組件
- `Navigation` - 導航列
- `PaginationControls` - 分頁控制
- `ProtectedRoute` - 路由保護
- `StatisticsCard` - 統計卡片
- `TabNavigation` - 標籤導航
- `Toast` - 通知系統
- `ValidatedTextInput` - 驗證輸入框
**設計評價**:⭐⭐⭐⭐⭐
- 組件職責單一,重用性高
- API 設計一致,易於使用
- 支援主題化和客製化
### 3.2 功能專用組件
**詞卡組件** (10個)
- 完整的 CRUD 操作組件
- 圖片生成和管理組件
- 搜尋和篩選組件
**複習組件** (20+個)
- 7 種複習模式的測試組件
- 統一的測試結果顯示
- 進度追蹤組件
**AI生成組件** (5個)
- 句子分析組件
- 詞彙選擇組件
- 慣用語展示組件
## 🔄 狀態管理分析
### 4.1 Zustand Store 設計
**架構優勢**
- 使用 `subscribeWithSelector` 中間件優化性能
- Store 按功能域分離,避免巨大單體
- 狀態更新邏輯集中化
**Store 職責劃分**
1. **useReviewSessionStore** - 複習會話核心狀態
- 當前詞卡、進度、模式設定
- 會話生命週期管理
2. **useTestQueueStore** - 測試佇列邏輯
- 智能優先級算法
- 測試順序管理
3. **useReviewDataStore** - 複習資料管理
- 待複習詞卡載入
- 數據快取機制
4. **useTestResultStore** - 測試結果追蹤
- 答題結果記錄
- 統計數據計算
5. **useUIStore** - UI 狀態管理
- 全域 UI 狀態
- 主題和設定
### 4.2 Hook 封裝層
**設計模式**Store 包裝器 + 業務邏輯
```typescript
// 範例useReviewSession Hook
export const useReviewSession = () => {
const store = useReviewSessionStore()
// 業務邏輯:載入下一張卡片
const loadNextCard = async () => {
try {
store.setLoading(true)
const result = await flashcardsService.getDueFlashcards()
store.setDueCards(result.data)
} catch (error) {
store.setError(error.message)
} finally {
store.setLoading(false)
}
}
return { ...store, loadNextCard }
}
```
## 🛠️ 服務層架構
### 5.1 API 服務設計
**統一 API 架構**
```typescript
// lib/services/flashcards.ts
class FlashcardsService {
private baseURL = 'http://localhost:5008'
// 統一的請求處理
private async makeRequest<T>(endpoint: string): Promise<ApiResponse<T>>
// 業務方法
async getFlashcards(): Promise<ApiResponse<Flashcard[]>>
async createFlashcard(data: CreateFlashcardRequest): Promise<ApiResponse<Flashcard>>
}
```
**優勢**
- 統一的錯誤處理機制
- 類型安全的 API 介面
- 支援開發/生產環境切換
### 5.2 工具函數層
**核心工具**
- `cefrUtils.ts` - CEFR 等級處理
- `flashcardUtils.ts` - 詞卡工具函數
- `testUtils.ts` - 測試相關工具
## 📈 性能分析
### 6.1 現況評估
**優勢**
- ✅ 使用 Next.js App Router 的最新優化
- ✅ Tailwind CSS 的 purge 機制
- ✅ TypeScript 的編譯時優化
**問題識別**
- ❌ 缺乏組件記憶化 (React.memo)
- ❌ 大型組件未進行代碼分割
- ❌ 圖片資源未優化 (WebP, 響應式)
- ❌ 無 Service Worker 快取機制
### 6.2 Bundle 分析 (推估)
**目前 Bundle 大小**
- **主應用**~2.5MB (開發模式)
- **複習模組**~800KB
- **AI生成模組**~400KB
- **詞卡模組**~600KB
## 🔧 具體優化建議
### 7.1 立即可實施 (本週)
1. **組件記憶化**
```typescript
// 高頻重渲染組件加上 React.memo
export const FlashcardCard = React.memo(FlashcardCard)
export const VocabularyStatsGrid = React.memo(VocabularyStatsGrid)
```
2. **圖片優化**
```typescript
// 添加圖片懶載入
<img loading="lazy" src={imageUrl} alt="..." />
```
3. **Hook 合併**
```typescript
// 合併相似的 TTS 播放邏輯
const useTTSManager = () => {
// 統一的 TTS 邏輯
}
```
### 7.2 短期優化 (2-4週)
1. **代碼分割**
```typescript
// 路由級別的懶載入
const ReviewPage = lazy(() => import('./review/page'))
const GeneratePage = lazy(() => import('./generate/page'))
```
2. **Store 重構**
```typescript
// 拆分大型 Store
const useTestQueueCore = create(...) // 核心邏輯
const useTestQueueUI = create(...) // UI 狀態
```
3. **API 快取**
```typescript
// 實施 SWR 或 React Query
const { data, error, mutate } = useSWR('/api/flashcards', fetcher)
```
### 7.3 中期重構 (1-2月)
1. **微前端模組化**
```typescript
// 按功能拆分獨立模組
apps/
├── flashcards-module/
├── review-module/
├── generate-module/
└── shell-app/
```
2. **設計系統**
```typescript
// 建立完整的設計系統
components/
├── atoms/ # 原子組件
├── molecules/ # 分子組件
├── organisms/ # 有機體組件
└── templates/ # 頁面模板
```
3. **測試體系**
```typescript
// 完整的測試覆蓋
__tests__/
├── components/ # 組件測試
├── hooks/ # Hook 測試
├── stores/ # Store 測試
└── e2e/ # E2E 測試
```
## 🎯 架構成熟度評分
| 分類 | 評分 | 說明 |
|-----|-----|------|
| **組件設計** | ⭐⭐⭐⭐⭐ | 模組化程度高,重用性良好 |
| **狀態管理** | ⭐⭐⭐⭐ | 分層清晰但複雜度稍高 |
| **類型安全** | ⭐⭐⭐⭐⭐ | TypeScript 覆蓋率完整 |
| **性能優化** | ⭐⭐⭐ | 基礎優化到位,進階優化待加強 |
| **測試覆蓋** | ⭐⭐ | 缺乏完整的測試體系 |
| **文檔完整** | ⭐⭐⭐ | 基礎文檔存在,詳細文檔不足 |
| **可維護性** | ⭐⭐⭐⭐ | 架構清晰,但複雜度需控制 |
**整體評分**:⭐⭐⭐⭐ (4/5) - **優秀的現代前端架構**
## 🔍 深度分析:雙層設計模式
### Hook + Store 雙層設計詳解
你提到的重點:**為什麼要 Hook 和 Store 兩層?**
**實際案例分析**
#### Store 層 (純狀態)
```typescript
// useReviewSessionStore.ts
{
currentCard: ExtendedFlashcard | null,
isLoading: boolean,
setCurrentCard: (card) => set({ currentCard: card }),
setLoading: (loading) => set({ isLoading: loading })
}
```
#### Hook 層 (業務邏輯)
```typescript
// useReviewSession.ts
export const useReviewSession = () => {
const store = useReviewSessionStore()
const loadNextCard = async () => {
store.setLoading(true) // Store 操作
const result = await flashcardsService.getDueFlashcards() // API 呼叫
store.setCurrentCard(result.data[0]) // Store 操作
store.setLoading(false) // Store 操作
}
return { ...store, loadNextCard } // Store + 業務邏輯
}
```
### 📊 雙層設計的價值
**優點**
1. **關注點分離**: Store 專注狀態Hook 專注邏輯
2. **重用性**: Store 可被多個 Hook 使用
3. **可測試性**: 可獨立測試狀態邏輯和業務邏輯
4. **擴展性**: 新功能容易添加新 Hook
**缺點**
1. **學習成本**: 開發者需理解兩層概念
2. **複雜度**: 簡單功能可能過度工程
3. **調試難度**: 狀態流向更複雜
### 🤔 是否過度設計?
**分析**
**適合雙層的場景**
- ✅ 複習系統:複雜狀態 + 複雜邏輯
- ✅ 詞卡管理:多種操作模式
- ✅ AI 生成:異步處理 + 狀態追蹤
**可能過度的場景**
- ❓ 簡單的 UI 狀態管理
- ❓ 一次性業務邏輯
- ❓ 純工具函數封裝
## 🚨 發現的架構問題
### 問題 1: Hook 邏輯重複
**現象**:多個組件都有相似的 TTS 播放邏輯
**影響**:代碼重複,維護成本高
**解決**:✅ 已通過 `BluePlayButton` 組件統一
### 問題 2: Store 複雜度過高
**現象**`useTestQueueStore` 有 394 行代碼
**影響**:難以理解和維護
**建議**:拆分為多個小 Store
### 問題 3: 類型定義分散
**現象**:類型定義在多個地方重複
**影響**:類型不一致,重構困難
**建議**:建立統一的類型註冊中心
### 問題 4: 缺乏性能優化
**現象**:沒有 React.memo, lazy loading
**影響**:不必要的重渲染
**建議**:實施組件記憶化策略
## 🎯 優化路線圖
### Phase 1: 立即優化 (1週)
- [ ] 組件記憶化 (React.memo)
- [ ] 圖片懶載入
- [ ] 統一 TTS 邏輯 ✅
### Phase 2: 短期優化 (2-4週)
- [ ] Store 拆分重構
- [ ] 代碼分割實施
- [ ] API 快取機制
### Phase 3: 中期重構 (1-2月)
- [ ] 設計系統建立
- [ ] 測試框架完整化
- [ ] 性能監控整合
### Phase 4: 長期規劃 (2-3月)
- [ ] 微前端架構
- [ ] PWA 功能
- [ ] 國際化支援
## 💡 關鍵建議
### 建議 1: 簡化狀態管理
**當前**Hook + Store 雙層
**建議**:評估是否需要簡化為單層(對於簡單狀態)
### 建議 2: 實施性能優化
**優先級**:🔴 高
- 添加 React.memo 到高頻組件
- 實施路由級代碼分割
- 圖片和資源優化
### 建議 3: 建立測試體系
**優先級**:🟡 中
- 單元測試 (Jest + RTL)
- 整合測試
- E2E 測試 (Playwright)
### 建議 4: 文檔完善
**優先級**:🟡 中
- Storybook 組件文檔
- API 文檔
- 架構決策記錄 (ADR)
## 📋 總結
DramaLing 前端架構**整體優秀**,採用現代的分層設計模式,組件化程度高,類型安全性強。
**核心優勢**
- 清晰的模組分離
- 良好的開發體驗
- 強大的功能完整性
**改進空間**
- 性能優化需要加強
- 複雜度控制需要平衡
- 測試和文檔需要完善
**推薦策略**
採用**漸進式重構**,優先解決性能問題,然後逐步優化架構複雜度,建立完整的測試和文檔體系。
---
*報告生成時間: 2025-10-01*
*分析範圍: Frontend 系統完整架構*
*建議更新頻率: 每月一次*

View File

@ -13,11 +13,11 @@ import { LoadingStates } from '@/components/review/LoadingStates'
import { ReviewRunner } from '@/components/review/ReviewRunner'
// 狀態管理
import { useReviewSessionStore } from '@/store/useReviewSessionStore'
import { useTestQueueStore } from '@/store/useTestQueueStore'
import { useTestResultStore } from '@/store/useTestResultStore'
import { useReviewDataStore } from '@/store/useReviewDataStore'
import { useUIStore } from '@/store/useUIStore'
import { useReviewSessionStore } from '@/store/review/useReviewSessionStore'
import { useTestQueueStore } from '@/store/review/useTestQueueStore'
import { useTestResultStore } from '@/store/review/useTestResultStore'
import { useReviewDataStore } from '@/store/review/useReviewDataStore'
import { useReviewUIStore } from '@/store/review/useReviewUIStore'
import { ReviewService } from '@/lib/services/review/reviewService'
export default function LearnPage() {
@ -53,7 +53,7 @@ export default function LearnPage() {
closeReportModal,
closeImageModal,
setReportReason
} = useUIStore()
} = useReviewUIStore()
// 初始化
useEffect(() => {

View File

@ -1,6 +1,6 @@
import React, { useState } from 'react'
import { useTestQueueStore } from '@/store/useTestQueueStore'
import { useTestResultStore } from '@/store/useTestResultStore'
import { useTestQueueStore } from '@/store/review/useTestQueueStore'
import { useTestResultStore } from '@/store/review/useTestResultStore'
import { mockFlashcards, getTestStatistics, generateTestQueue } from '@/data/mockTestData'
interface TestDebugPanelProps {

View File

@ -1,5 +1,5 @@
import React, { memo, useCallback, useMemo } from 'react'
import { useTestQueueStore } from '@/store/useTestQueueStore'
import { useTestQueueStore } from '@/store/review/useTestQueueStore'
/**
*

View File

@ -1,8 +1,8 @@
import { useEffect, useState, useCallback } from 'react'
import { useReviewSessionStore } from '@/store/useReviewSessionStore'
import { useTestQueueStore } from '@/store/useTestQueueStore'
import { useTestResultStore } from '@/store/useTestResultStore'
import { useUIStore } from '@/store/useUIStore'
import { useReviewSessionStore } from '@/store/review/useReviewSessionStore'
import { useTestQueueStore } from '@/store/review/useTestQueueStore'
import { useTestResultStore } from '@/store/review/useTestResultStore'
import { useReviewUIStore } from '@/store/review/useReviewUIStore'
import { SmartNavigationController } from './NavigationController'
import { ProgressBar } from './ProgressBar'
import { mockFlashcards } from '@/data/mockTestData'
@ -32,7 +32,7 @@ export const ReviewRunner: React.FC<TestRunnerProps> = ({ className }) => {
const {
openReportModal,
openImageModal
} = useUIStore()
} = useReviewUIStore()
// 重置答題狀態(切換測驗時)
useEffect(() => {

View File

@ -1,5 +1,5 @@
import React from 'react'
import { TestItem } from '@/store/useTestQueueStore'
import { TestItem } from '@/store/review/useTestQueueStore'
import { TestStatusIndicator, TestStats, TestProgressBar, TestStatusList } from './TestStatusIndicator'
interface TaskListModalProps {

View File

@ -1,5 +1,5 @@
import React, { memo } from 'react'
import { TestItem } from '@/store/useTestQueueStore'
import { TestItem } from '@/store/review/useTestQueueStore'
/**
*

View File

@ -3,7 +3,7 @@
* 便 Store Store
*/
import { useReviewSessionStore } from '@/store/useReviewSessionStore'
import { useReviewSessionStore } from '@/store/review/useReviewSessionStore'
import { flashcardsService } from '@/lib/services/flashcards'
import type { ExtendedFlashcard, ReviewMode } from '@/lib/types/review'

View File

@ -1,6 +1,6 @@
import { flashcardsService } from '@/lib/services/flashcards'
import { ExtendedFlashcard } from '@/lib/types/review'
import { TestItem } from '@/store/useTestQueueStore'
import { TestItem } from '@/store/review/useTestQueueStore'
// 複習會話服務
export class ReviewService {

View File

@ -12,14 +12,16 @@
- **型別安全**: 使用 TypeScript 確保型別安全
- **可測試性**: 小型、專注的 stores 更容易測試
### Store 分類
### Store 分類 (按功能模組組織)
```
/store/
├── useReviewSessionStore.ts # 會話狀態管理
├── useTestQueueStore.ts # 測試隊列管理
├── useTestResultStore.ts # 測試結果管理
├── useReviewDataStore.ts # 數據狀態管理
└── useUIStore.ts # UI 狀態管理
├── review/ # 複習功能相關 Store
│ ├── useReviewSessionStore.ts # 會話狀態管理
│ ├── useTestQueueStore.ts # 測試隊列管理
│ ├── useTestResultStore.ts # 測試結果管理
│ ├── useReviewDataStore.ts # 數據狀態管理
│ └── useReviewUIStore.ts # Review UI 狀態管理
└── README.md # 架構說明文檔
```
## 📚 各 Store 詳細說明

View File

@ -1,7 +1,7 @@
import { create } from 'zustand'
// UI 狀態管理
interface UIState {
// Review UI 狀態管理
interface ReviewUIState {
// Modal 狀態
showTaskListModal: boolean
showReportModal: boolean
@ -29,7 +29,7 @@ interface UIState {
closeImageModal: () => void
}
export const useUIStore = create<UIState>((set) => ({
export const useReviewUIStore = create<ReviewUIState>((set) => ({
// 初始狀態
showTaskListModal: false,
showReportModal: false,