重构前端代码,定义开发规划

Vue3 + TypeScript 组件开发架构规范

🏗️ 整体架构思路

分层架构设计

复制代码
┌─────────────────────────────────────┐
│        展示组件层 (Components)         │ ← UI渲染 + 用户交互
├─────────────────────────────────────┤
│        业务逻辑层 (Composable)        │ ← 业务逻辑 + 状态处理  
├─────────────────────────────────────┤
│        数据管理层 (Pinia Store)        │ ← 全局状态 + 数据持久化
├─────────────────────────────────────┤
│        类型定义层 (Types)             │ ← TypeScript接口定义
└─────────────────────────────────────┘

关注点分离原则

  • 组件层: 只关注 UI 渲染和用户交互,保持组件纯粹性
  • 逻辑层: 处理业务逻辑、数据转换、副作用管理
  • 数据层: 管理全局状态、API调用、数据缓存
  • 类型层: 提供完整的 TypeScript 类型支持

📁 目录结构规范

复制代码
src/
├── components/          # 展示组件层
│   └── reader/
│       ├── toolbar/
│       │   ├── AIToolbar.vue       # AI工具栏组件
│       │   ├── ReadingToolbar.vue  # 阅读工具栏
│       │   └── index.ts            # 组件导出
│       └── content/
│           ├── BookContent.vue     # 书籍内容组件
│           └── ChapterNav.vue      # 章节导航
├── composables/         # 业务逻辑层
│   ├── useAIToolbar.ts             # AI工具栏逻辑
│   ├── useReader.ts                # 阅读器核心逻辑
│   ├── usePreviousSummary.ts       # 前情提要逻辑
│   └── useBookmark.ts              # 书签功能逻辑
├── store/               # 数据管理层
│   ├── modules/
│   │   ├── reader.s.ts               # 阅读器状态管理
│   │   ├── ai.s.ts                   # AI功能状态管理
│   │   └── user.s.ts                 # 用户状态管理
│   └── index.ts                    # Store 入口
├── types/               # 类型定义层
│   ├── reader.d.ts                   # 阅读器相关类型
│   ├── ai.d.ts                       # AI功能类型
│   └── common.d.ts                   # 通用类型定义
└── api/                 # API接口层
    ├── reader.ts                   # 阅读器API
    └── ai.ts                       # AI功能API

🎯 各层职责详解

1. 展示组件层 (Components)

职责: UI渲染、事件处理、用户交互

vue 复制代码
<!-- components/reader/toolbar/AIToolbar.vue -->
<template>
  <div class="ai-toolbar">
    <el-button 
      @click="handleSummaryClick"
      :loading="isLoading"
      type="primary"
    >
      前情提要
    </el-button>
    <el-button @click="handleChatClick">AI对话</el-button>
  </div>
</template>

<script setup lang="ts">
import { useAIToolbar } from '@/composables/useAIToolbar'

const {
  isLoading,
  handleSummaryClick,
  handleChatClick
} = useAIToolbar()
</script>

2. 业务逻辑层 (Composables)

职责: 业务逻辑封装、状态管理、副作用处理

typescript 复制代码
// composables/useAIToolbar.ts
import { ref, computed } from 'vue'
import { useReaderStore } from '@/store/modules/reader'
import { useAIStore } from '@/store/modules/ai'
import type { PreviousSummary } from '@/types/ai'

export function useAIToolbar() {
  const readerStore = useReaderStore()
  const aiStore = useAIStore()
  
  const isLoading = ref(false)
  
  // 计算属性
  const currentBook = computed(() => readerStore.currentBook)
  const currentChapter = computed(() => readerStore.currentChapter)
  
  // 前情提要处理
  const handleSummaryClick = async () => {
    if (!currentBook.value || !currentChapter.value) return
    
    try {
      isLoading.value = true
      const summary = await aiStore.getPreviousSummary({
        bookId: currentBook.value.id,
        chapterId: currentChapter.value.id
      })
      
      // 显示前情提要弹窗
      aiStore.showSummaryDialog(summary)
    } catch (error) {
      console.error('获取前情提要失败:', error)
    } finally {
      isLoading.value = false
    }
  }
  
  // AI对话处理
  const handleChatClick = () => {
    aiStore.openChatDialog()
  }
  
  return {
    isLoading,
    handleSummaryClick,
    handleChatClick
  }
}

3. 数据管理层 (Pinia Store)

职责: 全局状态管理、API调用、数据缓存

typescript 复制代码
// store/modules/ai.ts
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
import { getPreviousSummaryAPI } from '@/api/ai'
import type { PreviousSummary, AIState } from '@/types/ai'

export const useAIStore = defineStore('ai', () => {
  // 状态定义
  const summaryCache = ref(new Map<string, PreviousSummary>())
  const isSummaryDialogVisible = ref(false)
  const currentSummary = ref<PreviousSummary | null>(null)
  
  // 计算属性
  const hasCachedSummary = computed(() => (bookId: string, chapterId: string) => {
    const key = `${bookId}-${chapterId}`
    return summaryCache.value.has(key)
  })
  
  // Actions
  const getPreviousSummary = async (params: { bookId: string, chapterId: string }) => {
    const cacheKey = `${params.bookId}-${params.chapterId}`
    
    // 检查缓存
    if (summaryCache.value.has(cacheKey)) {
      return summaryCache.value.get(cacheKey)!
    }
    
    // API调用
    const summary = await getPreviousSummaryAPI(params)
    
    // 缓存结果
    summaryCache.value.set(cacheKey, summary)
    
    return summary
  }
  
  const showSummaryDialog = (summary: PreviousSummary) => {
    currentSummary.value = summary
    isSummaryDialogVisible.value = true
  }
  
  const closeSummaryDialog = () => {
    isSummaryDialogVisible.value = false
    currentSummary.value = null
  }
  
  return {
    // 状态
    summaryCache,
    isSummaryDialogVisible,
    currentSummary,
    
    // 计算属性
    hasCachedSummary,
    
    // 方法
    getPreviousSummary,
    showSummaryDialog,
    closeSummaryDialog
  }
})

4. 类型定义层 (Types)

职责: TypeScript 类型定义、接口约束

typescript 复制代码
// types/ai.ts
export interface PreviousSummary {
  id: string
  bookId: string
  chapterId: string
  summary: string
  keyPoints: string[]
  chapterTitle: string
  createdAt: string
}

export interface AIDialogState {
  isVisible: boolean
  messages: ChatMessage[]
  isLoading: boolean
}

export interface ChatMessage {
  id: string
  role: 'user' | 'assistant'
  content: string
  timestamp: number
}

// API 请求/响应类型
export interface GetSummaryRequest {
  bookId: string
  chapterId: string
}

export interface GetSummaryResponse {
  code: number
  message: string
  data: PreviousSummary
}

🔄 数据流向示例

复制代码
用户点击"前情提要"按钮
         ↓
    组件触发事件处理
         ↓
  Composable 处理业务逻辑
         ↓
    Store 检查缓存
         ↓
  (无缓存)调用API获取数据
         ↓
   Store 更新状态并缓存
         ↓
 Composable 更新组件状态
         ↓
     组件重新渲染

📝 命名规范

组件命名

  • 大驼峰 + 语义化 : AIToolbar.vue, PreviousSummary.vue
  • 功能模块前缀 : Reader*, AI*, Book*

Composable 命名

  • use 前缀: useAIToolbar, useReader, usePreviousSummary
  • 功能描述: 清晰表达composable的主要功能

Store 命名

  • 功能模块 : reader, ai, user, book
  • 文件名小写 : reader.ts, ai.ts

类型命名

  • 接口大驼峰 : PreviousSummary, AIDialogState
  • 枚举大驼峰 : LoadingState, DialogType

✅ 最佳实践

1. 组件纯粹性

vue 复制代码
<!-- ✅ 好的做法 -->
<script setup lang="ts">
import { useAIToolbar } from '@/composables/useAIToolbar'

const { isLoading, handleClick } = useAIToolbar()
</script>

<!-- ❌ 避免在组件中直接调用API -->
<script setup lang="ts">
import { getPreviousSummaryAPI } from '@/api/ai'

const handleClick = async () => {
  const result = await getPreviousSummaryAPI(params) // 不应该在组件中直接调用
}
</script>

2. 状态管理

typescript 复制代码
// ✅ 在 Store 中管理全局状态
const useAIStore = defineStore('ai', () => {
  const globalAIState = ref<AIState>()
  return { globalAIState }
})

// ✅ 在 Composable 中管理局部状态
export function useDialog() {
  const isVisible = ref(false) // 局部组件状态
  return { isVisible }
}

3. 错误处理

typescript 复制代码
// ✅ 在 Composable 中统一处理错误
export function useAIToolbar() {
  const handleSummaryClick = async () => {
    try {
      await aiStore.getPreviousSummary(params)
    } catch (error) {
      ElMessage.error('获取前情提要失败')
      console.error(error)
    }
  }
}

4. 类型安全

typescript 复制代码
// ✅ 完整的类型定义
export function useAIToolbar(): {
  isLoading: Ref<boolean>
  handleSummaryClick: () => Promise<void>
  handleChatClick: () => void
} {
  // 实现...
}

🎯 开发流程

  1. 设计阶段: 先定义 types,明确数据结构
  2. 数据层: 实现 Store,定义状态管理逻辑
  3. 逻辑层: 开发 Composable,封装业务逻辑
  4. 展示层: 创建 Vue 组件,专注UI和交互
  5. 测试: 分层测试,确保各层职责清晰

通过这种架构,我们能够实现高内聚、低耦合的代码组织,便于维护和扩展。

相关推荐
你的人类朋友31 分钟前
🤔Token 存储方案有哪些
前端·javascript·后端
烛阴32 分钟前
从零开始:使用Node.js和Cheerio进行轻量级网页数据提取
前端·javascript·后端
liuyang___1 小时前
日期的数据格式转换
前端·后端·学习·node.js·node
贩卖纯净水.2 小时前
webpack其余配置
前端·webpack·node.js
码上奶茶2 小时前
HTML 列表、表格、表单
前端·html·表格·标签·列表·文本·表单
抹茶san2 小时前
和 Trae 一起开发可视化拖拽编辑项目(1) :迈出第一步
前端·trae
风吹头皮凉2 小时前
vue实现气泡词云图
前端·javascript·vue.js
南玖i3 小时前
vue3 + ant 实现 tree默认展开,筛选对应数据打开,简单~直接cv
开发语言·前端·javascript
小钻风33663 小时前
深入浅出掌握 Axios(持续更新)
前端·javascript·axios