最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Vue3+TypeScript項目架構實踐指南

 更新時間:2026年02月28日 08:50:20   作者:David涼宸  
在現(xiàn)代前端開發(fā)中,Vue3和Vite的組合已經(jīng)成為許多開發(fā)者的首選,這篇文章主要介紹了Vue3+TypeScript項目架構實踐的相關資料,文中通過代碼介紹的非常詳細,需要的朋友可以參考下

1. 引言

Vue 3 + TypeScript + Vite + Pinia 技術棧已經(jīng)成為現(xiàn)代前端開發(fā)的主流選擇,其優(yōu)秀的性能和開發(fā)體驗備受開發(fā)者青睞。然而,要充分發(fā)揮這一技術棧的潛力,合理的項目架構設計至關重要。

一個良好的項目架構能夠:

  • 提高代碼的可維護性和可擴展性
  • 減少團隊協(xié)作中的沖突和問題
  • 加速開發(fā)效率和代碼質量
  • 便于測試和部署

本文將詳細介紹 Vue 3 + TypeScript 項目的架構實踐,包括項目結構設計、狀態(tài)管理架構、路由架構、API 層設計、工具類和公共組件的組織,以及企業(yè)級項目案例分析。

2. 項目結構設計

2.1 目錄結構組織

一個合理的目錄結構是項目架構的基礎,它能夠清晰地分離不同功能模塊,便于開發(fā)者理解和維護代碼。

// 推薦的 Vue 3 + TypeScript 項目目錄結構
// 該結構清晰分離了不同功能模塊,便于開發(fā)者理解和維護代碼
// 每個目錄都有明確的職責和用途,遵循關注點分離原則
// 設計意圖:通過模塊化組織提高代碼可維護性和可擴展性
src/                             // 源代碼目錄
├── assets/            # 靜態(tài)資源文件
│   ├── images/        # 圖片資源(如產(chǎn)品圖片、背景圖等)
│   ├── styles/        # 全局樣式(如重置樣式、主題樣式等)
│   └── icons/         # 圖標資源(如 SVG 圖標、字體圖標等)
├── components/        # 公共組件
│   ├── common/        # 通用基礎組件(如按鈕、輸入框等)
│   ├── layout/        # 布局相關組件(如頭部、側邊欄等)
│   └── business/      # 業(yè)務相關組件(如用戶卡片、產(chǎn)品列表等)
├── composables/       # 組合式 API 邏輯
│   ├── useAuth.ts     # 認證相關邏輯(如登錄、注冊等)
│   ├── useApi.ts      # API 調用邏輯(如請求封裝、錯誤處理等)
│   └── useLocalStorage.ts # 本地存儲邏輯(如數(shù)據(jù)持久化等)
├── constants/         # 常量定義
│   ├── api.ts         # API 相關常量(如接口地址、請求超時等)
│   ├── routes.ts      # 路由相關常量(如路由名稱、路徑等)
│   └── storage.ts     # 存儲相關常量(如存儲鍵名、過期時間等)
├── enums/             # 枚舉類型定義
│   ├── user.ts        # 用戶相關枚舉(如用戶角色、狀態(tài)等)
│   └── status.ts      # 狀態(tài)相關枚舉(如訂單狀態(tài)、審批狀態(tài)等)
├── hooks/             # 自定義鉤子(與 composables 類似,可根據(jù)團隊習慣選擇其一)
├── layouts/           # 布局組件
│   ├── DefaultLayout.vue # 默認布局(如包含側邊欄和頭部的布局)
│   └── AuthLayout.vue    # 認證布局(如登錄、注冊頁面的布局)
├── models/            # 數(shù)據(jù)模型定義
│   ├── user.ts        # 用戶模型(如用戶信息結構、類型定義等)
│   ├── product.ts     # 產(chǎn)品模型(如產(chǎn)品信息結構、類型定義等)
│   └── common.ts      # 通用模型(如分頁結構、響應結構等)
├── router/            # 路由配置
│   ├── index.ts       # 路由主配置(如創(chuàng)建路由實例、應用守衛(wèi)等)
│   ├── routes.ts      # 路由定義(如路由路徑、組件映射等)
│   └── guards.ts      # 路由守衛(wèi)(如認證守衛(wèi)、權限守衛(wèi)等)
├── services/          # 服務層
│   ├── api/           # API 服務
│   │   ├── user.ts    # 用戶相關 API(如登錄、獲取用戶信息等)
│   │   ├── product.ts # 產(chǎn)品相關 API(如獲取產(chǎn)品列表、創(chuàng)建產(chǎn)品等)
│   │   └── index.ts   # API 服務主文件(如創(chuàng)建 axios 實例、配置攔截器等)
│   └── utils/         # 工具服務(如第三方服務集成、業(yè)務工具等)
├── stores/            # Pinia 狀態(tài)管理
│   ├── user.ts        # 用戶狀態(tài)(如用戶信息、認證狀態(tài)等)
│   ├── product.ts     # 產(chǎn)品狀態(tài)(如產(chǎn)品列表、詳情等)
│   └── common.ts      # 通用狀態(tài)(如全局加載狀態(tài)、錯誤信息等)
├── types/             # TypeScript 類型定義
│   ├── api.ts         # API 相關類型(如請求參數(shù)、響應類型等)
│   ├── components.ts  # 組件相關類型(如 props 類型、事件類型等)
│   └── common.ts      # 通用類型(如通用接口、工具類型等)
├── utils/             # 工具函數(shù)
│   ├── format.ts      # 格式化工具(如日期格式化、金額格式化等)
│   ├── validation.ts  # 驗證工具(如郵箱驗證、密碼強度驗證等)
│   └── storage.ts     # 存儲工具(如本地存儲封裝、會話存儲封裝等)
├── views/             # 頁面組件
│   ├── auth/          # 認證相關頁面
│   │   ├── Login.vue  # 登錄頁面
│   │   └── Register.vue # 注冊頁面
│   ├── dashboard/     # 儀表盤頁面
│   └── products/      # 產(chǎn)品相關頁面
├── App.vue            # 根組件(應用的入口組件)
├── main.ts            # 應用入口(如初始化 Vue 應用、注冊插件等)
└── env.d.ts           # 環(huán)境變量類型定義(如 Vite 環(huán)境變量類型聲明)

2.2 目錄結構設計原則

  1. 功能模塊化:將相關功能的代碼組織在一起,便于理解和維護
  2. 關注點分離:將不同職責的代碼分離到不同目錄,如視圖、組件、狀態(tài)管理等
  3. 層次清晰:建立清晰的代碼層次結構,如 API 層、服務層、業(yè)務邏輯層等
  4. 可擴展性:預留合理的擴展空間,便于后續(xù)功能的添加和修改
  5. 命名規(guī)范:采用一致的命名規(guī)范,提高代碼的可讀性

2.3 模塊劃分策略

按業(yè)務功能劃分

將代碼按照業(yè)務功能進行劃分,每個功能模塊包含完整的組件、狀態(tài)、服務等。

// 按業(yè)務功能劃分的目錄結構
// 該結構將代碼按照業(yè)務功能進行劃分,每個功能模塊包含完整的組件、狀態(tài)、服務等
// 優(yōu)點:業(yè)務邏輯內聚,便于團隊協(xié)作和代碼維護
// 缺點:可能導致某些通用代碼重復

// 適用場景:大型應用,多個團隊負責不同業(yè)務模塊
// 設計意圖:通過業(yè)務模塊劃分,實現(xiàn)團隊協(xié)作的隔離和業(yè)務邏輯的內聚
src/
├── modules/            # 業(yè)務模塊目錄(存放各個獨立的業(yè)務模塊)
│   ├── auth/          # 認證模塊(處理登錄、注冊等認證相關功能)
│   │   ├── components/ # 認證相關組件(如登錄表單、注冊表單等)
│   │   ├── views/      # 認證相關頁面(如登錄頁面、注冊頁面等)
│   │   ├── services/   # 認證相關服務(如認證 API 調用等)
│   │   └── stores/     # 認證相關狀態(tài)(如認證狀態(tài)管理等)
│   ├── product/        # 產(chǎn)品模塊(處理產(chǎn)品相關功能)
│   │   ├── components/ # 產(chǎn)品相關組件(如產(chǎn)品卡片、產(chǎn)品列表項等)
│   │   ├── views/      # 產(chǎn)品相關頁面(如產(chǎn)品列表頁、產(chǎn)品詳情頁等)
│   │   ├── services/   # 產(chǎn)品相關服務(如產(chǎn)品 API 調用等)
│   │   └── stores/     # 產(chǎn)品相關狀態(tài)(如產(chǎn)品列表狀態(tài)、詳情狀態(tài)等)
│   └── user/           # 用戶模塊(處理用戶相關功能)
│       ├── components/ # 用戶相關組件(如用戶信息卡片、用戶列表項等)
│       ├── views/      # 用戶相關頁面(如用戶列表頁、用戶詳情頁等)
│       ├── services/   # 用戶相關服務(如用戶 API 調用等)
│       └── stores/     # 用戶相關狀態(tài)(如用戶列表狀態(tài)、詳情狀態(tài)等)
└── shared/             # 共享資源(存放各個模塊共用的代碼)
    ├── components/     # 共享組件(如通用按鈕、輸入框等)
    ├── composables/    # 共享組合式 API(如通用認證邏輯、API 調用邏輯等)
    ├── utils/          # 共享工具函數(shù)(如格式化工具、驗證工具等)
    └── types/          # 共享類型定義(如通用接口、類型聲明等)

按技術類型劃分

將代碼按照技術類型進行劃分,如組件、服務、狀態(tài)管理等。

// 按技術類型劃分的目錄結構
// 該結構將代碼按照技術類型進行劃分,如組件、服務、狀態(tài)管理等
// 優(yōu)點:技術職責清晰,便于代碼復用和維護
// 缺點:業(yè)務邏輯分散在不同目錄,可能增加跨模塊理解難度

// 適用場景:中小型應用,團隊成員技術棧全面
// 設計意圖:通過技術類型劃分,實現(xiàn)代碼的分類管理和復用
src/
├── components/         # 所有組件(按技術類型組織的組件集合)
├── services/           # 所有服務(按技術類型組織的服務集合)
├── stores/             # 所有狀態(tài)管理(按技術類型組織的狀態(tài)管理集合)
├── views/              # 所有頁面(按技術類型組織的頁面集合)
└── utils/              # 所有工具函數(shù)(按技術類型組織的工具函數(shù)集合)

混合劃分策略

結合業(yè)務功能和技術類型的劃分策略,在頂層按技術類型劃分,在具體模塊內按業(yè)務功能劃分。

// 混合劃分策略的目錄結構
// 該結構結合業(yè)務功能和技術類型的劃分策略,在頂層按技術類型劃分,在具體模塊內按業(yè)務功能劃分
// 優(yōu)點:既保持了技術職責清晰,又保證了業(yè)務邏輯的內聚性
// 缺點:目錄結構可能相對復雜

// 適用場景:中大型應用,需要平衡技術管理和業(yè)務邏輯
// 設計意圖:通過混合劃分策略,兼顧技術管理的清晰性和業(yè)務邏輯的內聚性
src/
├── components/         # 所有組件(頂層按技術類型劃分)
│   ├── common/         # 通用組件(如按鈕、輸入框等通用基礎組件)
│   ├── auth/           # 認證相關組件(如登錄表單、注冊表單等)
│   └── product/        # 產(chǎn)品相關組件(如產(chǎn)品卡片、產(chǎn)品列表等)
├── services/           # 所有服務(頂層按技術類型劃分)
│   ├── api/            # API 服務(按業(yè)務功能劃分的 API 服務)
│   │   ├── auth.ts     # 認證 API(處理認證相關的 API 調用)
│   │   └── product.ts  # 產(chǎn)品 API(處理產(chǎn)品相關的 API 調用)
│   └── utils/          # 服務工具(如 API 請求封裝、錯誤處理等)
└── stores/             # 所有狀態(tài)管理(頂層按技術類型劃分)
    ├── auth.ts         # 認證狀態(tài)(處理認證相關的狀態(tài)管理)
    └── product.ts      # 產(chǎn)品狀態(tài)(處理產(chǎn)品相關的狀態(tài)管理)

2.4 推薦的目錄結構

根據(jù)項目規(guī)模和團隊習慣,推薦使用以下目錄結構:

// 推薦的目錄結構
// 該結構根據(jù)項目規(guī)模和團隊習慣,綜合考慮了技術管理和業(yè)務邏輯的平衡
// 每個目錄都有明確的職責,便于開發(fā)者理解和維護代碼

// 適用場景:大多數(shù) Vue 3 + TypeScript 項目
// 設計意圖:提供一個通用的、可擴展的目錄結構,適用于各種規(guī)模的項目
src/
├── assets/            # 靜態(tài)資源(如圖片、樣式、圖標等)
├── components/        # 公共組件(如通用組件、業(yè)務組件等)
├── composables/       # 組合式 API(如認證邏輯、API 調用邏輯等)
├── constants/         # 常量定義(如 API 地址、路由名稱等)
├── enums/             # 枚舉類型(如用戶角色、訂單狀態(tài)等)
├── layouts/           # 布局組件(如默認布局、認證布局等)
├── models/            # 數(shù)據(jù)模型(如用戶模型、產(chǎn)品模型等)
├── router/            # 路由配置(如路由定義、守衛(wèi)等)
├── services/          # 服務層(如 API 服務、工具服務等)
├── stores/            # Pinia 狀態(tài)管理(如用戶狀態(tài)、產(chǎn)品狀態(tài)等)
├── types/             # TypeScript 類型(如 API 類型、組件類型等)
├── utils/             # 工具函數(shù)(如格式化工具、驗證工具等)
├── views/             # 頁面組件(如認證頁面、儀表盤頁面等)
├── App.vue            # 根組件(應用的入口組件)
├── main.ts            # 入口文件(初始化 Vue 應用、注冊插件等)
└── env.d.ts           # 環(huán)境變量類型(Vite 環(huán)境變量類型聲明)

3. 狀態(tài)管理架構

3.1 Pinia 狀態(tài)管理實踐

Pinia 作為 Vue 3 官方推薦的狀態(tài)管理庫,提供了更簡潔、更靈活的狀態(tài)管理方案。

狀態(tài)管理結構設計

  1. 按模塊劃分 Store:將狀態(tài)按照業(yè)務模塊進行劃分,每個模塊對應一個 Store
  2. 使用 Composition API:采用 Composition API 風格定義 Store,提高代碼的可讀性和可維護性
  3. 類型安全:充分利用 TypeScript 的類型系統(tǒng),為 Store 定義明確的類型

Store 組織方式

// stores/user.ts
// 用戶狀態(tài)管理 Store
// 該文件使用 Pinia 的 Composition API 風格定義用戶相關的狀態(tài)管理
// 包含用戶信息、認證狀態(tài)、登錄/登出等功能
// 特點:類型安全,代碼結構清晰,邏輯組織合理
// 設計意圖:通過 Pinia 管理用戶認證狀態(tài)和用戶信息,實現(xiàn)狀態(tài)的集中管理和持久化

import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
import { User } from '@/models/user'
import { userApi } from '@/services/api/user'

/**
 * 用戶狀態(tài)管理 Store
 * 管理用戶認證狀態(tài)、用戶信息等
 * 使用 Composition API 風格定義,提供更好的類型推斷和代碼組織
 * @returns 用戶狀態(tài)管理 Store 實例
 */
export const useUserStore = defineStore('user', () => {
  // 狀態(tài)(State)
  const currentUser = ref<User | null>(null) // 當前用戶信息,類型為 User 或 null
  const token = ref<string | null>(null) // 認證令牌,用于 API 調用的身份驗證
  const loading = ref(false) // 加載狀態(tài),用于控制加載指示器的顯示
  const error = ref<string | null>(null) // 錯誤信息,用于顯示錯誤提示

  // Getters(計算屬性)
  const isAuthenticated = computed(() => !!token.value) // 是否已認證,根據(jù) token 是否存在判斷
  const userDisplayName = computed(() => {
    return currentUser.value ? `${currentUser.value.firstName} ${currentUser.value.lastName}` : ''
  }) // 用戶顯示名稱,組合 firstName 和 lastName

  // Actions(方法)
  /**
   * 用戶登錄
   * @param email 郵箱
   * @param password 密碼
   * @returns 登錄響應,包含用戶信息和令牌
   * @throws 登錄失敗時拋出錯誤
   */
  async function login(email: string, password: string) {
    loading.value = true // 開始加載
    error.value = null // 清空錯誤信息
    try {
      const response = await userApi.login({ email, password }) // 調用登錄 API
      currentUser.value = response.user // 更新當前用戶信息
      token.value = response.token // 更新認證令牌
      localStorage.setItem('token', response.token) // 持久化存儲令牌到 localStorage
      return response // 返回登錄響應
    } catch (err) {
      error.value = 'Login failed' // 設置錯誤信息
      throw err // 重新拋出錯誤,以便調用方處理
    } finally {
      loading.value = false // 結束加載
    }
  }

  /**
   * 用戶登出
   * @throws 登出失敗時拋出錯誤
   */
  async function logout() {
    loading.value = true // 開始加載
    try {
      await userApi.logout() // 調用登出 API
      currentUser.value = null // 清空當前用戶信息
      token.value = null // 清空認證令牌
      localStorage.removeItem('token') // 清除 localStorage 中的令牌
    } catch (err) {
      error.value = 'Logout failed' // 設置錯誤信息
      throw err // 重新拋出錯誤,以便調用方處理
    } finally {
      loading.value = false // 結束加載
    }
  }

  /**
   * 獲取當前用戶信息
   * @returns 用戶信息
   * @throws 獲取失敗時拋出錯誤
   */
  async function fetchCurrentUser() {
    loading.value = true // 開始加載
    error.value = null // 清空錯誤信息
    try {
      const response = await userApi.getCurrentUser() // 調用獲取用戶信息 API
      currentUser.value = response // 更新當前用戶信息
      return response // 返回用戶信息
    } catch (err) {
      error.value = 'Failed to fetch user' // 設置錯誤信息
      throw err // 重新拋出錯誤,以便調用方處理
    } finally {
      loading.value = false // 結束加載
    }
  }

  /**
   * 初始化用戶狀態(tài)
   * 從本地存儲恢復令牌并獲取用戶信息
   * 在應用啟動時調用,用于保持用戶登錄狀態(tài)
   */
  function initialize() {
    const storedToken = localStorage.getItem('token') // 從 localStorage 獲取存儲的令牌
    if (storedToken) {
      token.value = storedToken // 更新認證令牌
      fetchCurrentUser() // 獲取用戶信息
    }
  }

  return {
    // 狀態(tài)(State)
    currentUser,
    token,
    loading,
    error,
    // Getters(計算屬性)
    isAuthenticated,
    userDisplayName,
    // Actions(方法)
    login,
    logout,
    fetchCurrentUser,
    initialize
  }
})

3.2 跨 Store 通信策略

在復雜的應用中,不同 Store 之間可能需要進行通信。以下是幾種跨 Store 通信的策略:

1. 直接引用其他 Store

在一個 Store 中直接引用和使用另一個 Store。

// stores/cart.ts
// 購物車狀態(tài)管理 Store
// 該文件演示了如何通過直接引用其他 Store 來實現(xiàn)跨 Store 通信
// 優(yōu)點:代碼簡潔直接,易于理解
// 缺點:Store 之間耦合度較高
// 設計意圖:演示跨 Store 通信的直接引用方法,適用于簡單場景

import { defineStore } from 'pinia'
import { useUserStore } from './user'

/**
 * 購物車狀態(tài)管理 Store
 * 管理購物車商品、數(shù)量等
 * @returns 購物車狀態(tài)管理 Store 實例
 */
export const useCartStore = defineStore('cart', () => {
  // 直接引用用戶 Store
  // 優(yōu)點:代碼簡潔直接,易于理解
  // 缺點:Store 之間耦合度較高,可能導致循環(huán)依賴
  const userStore = useUserStore()

  /**
   * 獲取用戶購物車
   * 根據(jù)用戶認證狀態(tài)獲取對應的購物車數(shù)據(jù)
   * 演示了如何使用其他 Store 的狀態(tài)(isAuthenticated)
   */
  function getUserCart() {
    if (userStore.isAuthenticated) {
      // 獲取登錄用戶的購物車
      // 當用戶已認證時,獲取與用戶賬號關聯(lián)的購物車
      console.log('Getting cart for authenticated user')
    } else {
      // 獲取訪客購物車
      // 當用戶未認證時,獲取基于本地存儲的訪客購物車
      console.log('Getting cart for guest user')
    }
  }

  return {
    getUserCart
  }
})

2. 使用事件總線

通過事件總線在不同 Store 之間傳遞消息。

// utils/eventBus.ts
// 事件總線工具
// 用于在不同組件和 Store 之間傳遞消息
// 基于 mitt 庫實現(xiàn)
// 設計意圖:通過事件總線實現(xiàn)組件和 Store 之間的解耦通信

import mitt from 'mitt'

/**
 * 事件總線實例
 * 用于跨組件和跨 Store 通信
 * 優(yōu)點:組件和 Store 之間解耦,減少直接依賴
 * 缺點:事件流難以追蹤,可能導致調試困難
 */
export const eventBus = mitt()

// stores/user.ts
// 用戶狀態(tài)管理 Store
// 該文件演示了如何通過事件總線發(fā)送事件
// 設計意圖:登錄成功后通知其他模塊,實現(xiàn)跨模塊通信

import { defineStore } from 'pinia'
import { eventBus } from '@/utils/eventBus'

/**
 * 用戶狀態(tài)管理 Store
 * @returns 用戶狀態(tài)管理 Store 實例
 */
export const useUserStore = defineStore('user', () => {
  /**
   * 用戶登錄
   * 登錄成功后通過事件總線通知其他 Store
   * 演示了如何通過事件總線發(fā)送事件
   */
  function login() {
    // 登錄邏輯
    console.log('User logging in...')
    // 登錄成功后發(fā)送事件
    // 事件名稱:'user:login',事件數(shù)據(jù):{ userId: 123 }
    eventBus.emit('user:login', { userId: 123 })
  }

  return {
    login
  }
})

// stores/cart.ts
// 購物車狀態(tài)管理 Store
// 該文件演示了如何通過事件總線監(jiān)聽事件
// 設計意圖:監(jiān)聽用戶登錄事件,同步購物車數(shù)據(jù)

import { defineStore } from 'pinia'
import { eventBus } from '@/utils/eventBus'

/**
 * 購物車狀態(tài)管理 Store
 * @returns 購物車狀態(tài)管理 Store 實例
 */
export const useCartStore = defineStore('cart', () => {
  /**
   * 初始化購物車
   * 監(jiān)聽用戶登錄事件,以便同步購物車數(shù)據(jù)
   * 演示了如何通過事件總線監(jiān)聽事件
   */
  function initialize() {
    // 監(jiān)聽用戶登錄事件
    // 事件名稱:'user:login',回調函數(shù)處理事件數(shù)據(jù)
    eventBus.on('user:login', (data) => {
      // 處理用戶登錄事件
      console.log('User logged in:', data.userId)
      // 可以在這里同步購物車數(shù)據(jù)
      // 例如:將訪客購物車數(shù)據(jù)同步到登錄用戶的購物車
    })
  }

  return {
    initialize
  }
})

3. 使用 Composition API 共享邏輯

將共享邏輯提取到 composables 中,供多個 Store 使用。

// composables/useApi.ts
// API 調用邏輯
// 該文件演示了如何通過 Composition API 封裝共享邏輯
// 優(yōu)點:邏輯復用性高,Store 之間耦合度低
// 設計意圖:封裝 API 請求的通用邏輯,供多個 Store 和組件復用

import { ref } from 'vue'

/**
 * API 調用邏輯
 * 封裝了 API 請求的通用邏輯,如加載狀態(tài)、錯誤處理等
 * 使用 Composition API 風格,提供更好的代碼組織和復用性
 * @returns API 調用相關的狀態(tài)和方法
 */
export function useApi() {
  const loading = ref(false) // 加載狀態(tài),用于控制加載指示器的顯示
  const error = ref<string | null>(null) // 錯誤信息,用于顯示錯誤提示

  /**
   * 發(fā)起 API 請求
   * 封裝了 API 請求的通用邏輯,包括加載狀態(tài)管理和錯誤處理
   * @param apiCall API 調用函數(shù),返回 Promise
   * @returns API 響應數(shù)據(jù)
   * @throws API 請求失敗時拋出錯誤
   */
  async function request<T>(apiCall: () => Promise<T>): Promise<T> {
    loading.value = true // 開始加載
    error.value = null // 清空錯誤信息
    try {
      return await apiCall() // 執(zhí)行 API 調用
    } catch (err) {
      error.value = 'API request failed' // 設置錯誤信息
      throw err // 重新拋出錯誤,以便調用方處理
    } finally {
      loading.value = false // 結束加載
    }
  }

  return {
    loading, // 加載狀態(tài)
    error, // 錯誤信息
    request // API 請求方法
  }
}

// stores/user.ts
// 用戶狀態(tài)管理 Store
// 該文件演示了如何通過 Composition API 共享邏輯
// 設計意圖:通過使用共享的 useApi composable,減少代碼重復,提高可維護性

import { defineStore } from 'pinia'
import { useApi } from '@/composables/useApi'
import { userApi } from '@/services/api/user' // 假設已導入

/**
 * 用戶狀態(tài)管理 Store
 * @returns 用戶狀態(tài)管理 Store 實例
 */
export const useUserStore = defineStore('user', () => {
  // 使用共享的 API 邏輯
  // 優(yōu)點:代碼復用,減少重復邏輯,Store 之間耦合度低
  const { loading, error, request } = useApi()

  /**
   * 用戶登錄
   * 使用共享的 request 方法發(fā)起登錄請求
   * 演示了如何使用共享的 composable 邏輯
   * @param email 郵箱
   * @param password 密碼
   * @returns 登錄響應,包含用戶信息和令牌
   * @throws 登錄失敗時拋出錯誤
   */
  async function login(email: string, password: string) {
    return request(() => userApi.login({ email, password }))
  }

  return {
    loading, // 加載狀態(tài)
    error, // 錯誤信息
    login // 登錄方法
  }
})

3.3 狀態(tài)持久化策略

對于需要持久化的狀態(tài),如用戶認證信息、用戶偏好設置等,可以使用以下策略:

1. 使用 localStorage/sessionStorage

// stores/user.ts
// 用戶狀態(tài)管理 Store
// 該文件演示了如何通過 localStorage 實現(xiàn)狀態(tài)持久化
// 優(yōu)點:實現(xiàn)簡單直接,適用于基本的狀態(tài)持久化需求
// 缺點:需要手動管理存儲邏輯,代碼冗余
// 設計意圖:通過 localStorage 實現(xiàn)狀態(tài)持久化,保持用戶登錄狀態(tài)

import { defineStore } from 'pinia'
import { ref } from 'vue'

/**
 * 用戶狀態(tài)管理 Store
 * @returns 用戶狀態(tài)管理 Store 實例
 */
export const useUserStore = defineStore('user', () => {
  // 從 localStorage 初始化令牌
  // 在 Store 初始化時,從 localStorage 中讀取之前存儲的令牌
  const token = ref<string | null>(localStorage.getItem('token'))

  /**
   * 設置令牌
   * 同時更新 localStorage 中的令牌,實現(xiàn)狀態(tài)持久化
   * @param newToken 新令牌,字符串或 null
   */
  function setToken(newToken: string | null) {
    token.value = newToken // 更新內存中的令牌狀態(tài)
    if (newToken) {
      localStorage.setItem('token', newToken) // 存儲令牌到 localStorage
    } else {
      localStorage.removeItem('token') // 清除 localStorage 中的令牌
    }
  }

  return {
    token, // 令牌狀態(tài)
    setToken // 設置令牌的方法
  }
})

2. 使用 pinia-plugin-persistedstate

// stores/user.ts
// 用戶狀態(tài)管理 Store
// 該文件演示了如何通過 pinia-plugin-persistedstate 實現(xiàn)狀態(tài)持久化
// 優(yōu)點:配置簡單,自動管理存儲邏輯,代碼簡潔
// 缺點:需要安裝額外的插件
// 設計意圖:通過 pinia-plugin-persistedstate 插件實現(xiàn)狀態(tài)持久化,簡化存儲邏輯

import { defineStore } from 'pinia'
import { ref } from 'vue'
import type { User } from '@/models/user' // 假設已導入

/**
 * 用戶狀態(tài)管理 Store
 * @returns 用戶狀態(tài)管理 Store 實例
 */
export const useUserStore = defineStore('user', () => {
  const currentUser = ref<User | null>(null) // 當前用戶信息,類型為 User 或 null
  const token = ref<string | null>(null) // 認證令牌,用于 API 調用的身份驗證

  /**
   * 用戶登錄
   * @param user 用戶信息,類型為 User
   * @param userToken 認證令牌,字符串
   */
  function login(user: User, userToken: string) {
    currentUser.value = user // 更新當前用戶信息
    token.value = userToken // 更新認證令牌
    // 由于配置了 persist,狀態(tài)會自動持久化到 localStorage
  }

  /**
   * 用戶登出
   */
  function logout() {
    currentUser.value = null // 清空當前用戶信息
    token.value = null // 清空認證令牌
    // 由于配置了 persist,狀態(tài)會自動從 localStorage 中移除
  }

  return {
    currentUser, // 當前用戶信息
    token, // 認證令牌
    login, // 登錄方法
    logout // 登出方法
  }
}, {
  persist: {
    key: 'user-store', // 存儲鍵名,用于在 localStorage 中標識存儲的數(shù)據(jù)
    storage: localStorage, // 存儲方式,使用 localStorage 實現(xiàn)持久化
    paths: ['currentUser', 'token'] // 需要持久化的狀態(tài)路徑,只持久化 currentUser 和 token
  }
})

4. 路由架構

4.1 路由配置組織

一個合理的路由配置能夠清晰地定義應用的導航結構,便于開發(fā)者理解和維護。

路由配置文件結構

// router/index.ts
// 路由主配置文件
// 該文件負責創(chuàng)建路由實例并應用全局守衛(wèi)
// 設計意圖:配置和初始化 Vue Router,應用全局守衛(wèi)

import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'
import { routes } from './routes'
import { authGuard } from './guards'

/**
 * 創(chuàng)建路由實例
 * 配置路由的歷史模式和路由表
 * @returns 路由實例
 */
const router = createRouter({
  history: createWebHistory(), // 使用 HTML5 History API,移除 URL 中的 # 符號
  routes // 路由配置,從 ./routes 文件導入
})

/**
 * 全局前置守衛(wèi)
 * 應用認證守衛(wèi),處理所有路由的認證和權限檢查
 */
router.beforeEach(authGuard)

export default router

// router/routes.ts
// 路由定義文件
// 該文件負責定義應用的所有路由
// 設計意圖:集中管理應用的路由配置,包括路由路徑、組件映射、元信息等

import { RouteRecordRaw } from 'vue-router'
import DefaultLayout from '@/layouts/DefaultLayout.vue'
import AuthLayout from '@/layouts/AuthLayout.vue'

/**
 * 路由元信息接口
 * 定義路由的額外信息,如認證要求、頁面標題、角色權限等
 */
export interface RouteMeta {
  /**
   * 是否需要認證
   * @default true
   */
  requiresAuth?: boolean
  /**
   * 頁面標題
   */
  title?: string
  /**
   * 角色權限
   * 只有具有指定角色的用戶才能訪問該路由
   */
  roles?: string[]
  /**
   * 是否在側邊欄顯示
   */
  sidebar?: boolean
  /**
   * 側邊欄圖標
   * 用于側邊欄導航的圖標名稱
   */
  icon?: string
}

/**
 * 擴展的路由記錄類型
 * 集成自定義的元信息接口,提供更好的類型支持
 */
export type AppRouteRecordRaw = RouteRecordRaw & {
  meta?: RouteMeta
  children?: AppRouteRecordRaw[]
}

/**
 * 路由配置數(shù)組
 * 定義應用的所有路由,包括布局、頁面組件、元信息等
 */
export const routes: AppRouteRecordRaw[] = [
  {
    path: '/',
    component: DefaultLayout, // 使用默認布局(包含側邊欄和頭部)
    meta: {
      requiresAuth: true // 需要認證
    },
    children: [
      {
        path: '',
        name: 'Dashboard',
        component: () => import('@/views/dashboard/index.vue'), // 懶加載儀表盤頁面
        meta: {
          title: '儀表盤',
          sidebar: true, // 在側邊欄顯示
          icon: 'dashboard' // 側邊欄圖標
        }
      },
      {
        path: 'products',
        name: 'Products',
        component: () => import('@/views/products/index.vue'), // 懶加載產(chǎn)品管理頁面
        meta: {
          title: '產(chǎn)品管理',
          sidebar: true, // 在側邊欄顯示
          icon: 'shopping-cart' // 側邊欄圖標
        }
      },
      {
        path: 'users',
        name: 'Users',
        component: () => import('@/views/users/index.vue'), // 懶加載用戶管理頁面
        meta: {
          title: '用戶管理',
          sidebar: true, // 在側邊欄顯示
          icon: 'users', // 側邊欄圖標
          roles: ['admin'] // 只有 admin 角色可以訪問
        }
      }
    ]
  },
  {
    path: '/auth',
    component: AuthLayout, // 使用認證布局(簡潔布局,無側邊欄)
    meta: {
      requiresAuth: false // 不需要認證
    },
    children: [
      {
        path: 'login',
        name: 'Login',
        component: () => import('@/views/auth/login.vue'), // 懶加載登錄頁面
        meta: {
          title: '登錄'
        }
      },
      {
        path: 'register',
        name: 'Register',
        component: () => import('@/views/auth/register.vue'), // 懶加載注冊頁面
        meta: {
          title: '注冊'
        }
      }
    ]
  },
  {
    path: '/:pathMatch(.*)*',
    name: 'NotFound',
    component: () => import('@/views/error/404.vue'), // 懶加載 404 頁面
    meta: {
      title: '頁面不存在'
    }
  }
]

// router/guards.ts
// 路由守衛(wèi)文件
// 該文件負責定義路由守衛(wèi)邏輯
// 設計意圖:實現(xiàn)路由的認證和權限控制,保護需要認證的路由

import { NavigationGuardNext, RouteLocationNormalized } from 'vue-router'
import { useUserStore } from '@/stores/user'

/**
 * 認證守衛(wèi)
 * 處理路由的認證和權限檢查
 * 在每次路由導航前執(zhí)行,確保用戶有足夠的權限訪問目標路由
 * @param to 目標路由對象
 * @param from 來源路由對象
 * @param next 導航函數(shù),決定導航是否繼續(xù)
 */
export function authGuard(to: RouteLocationNormalized, from: RouteLocationNormalized, next: NavigationGuardNext) {
  const userStore = useUserStore() // 獲取用戶狀態(tài)管理 Store
  
  // 設置頁面標題
  if (to.meta.title) {
    document.title = `${to.meta.title} - My App`
  }
  
  // 權限檢查
  const requiresAuth = to.meta.requiresAuth !== false // 默認需要認證
  const isAuthenticated = userStore.isAuthenticated // 用戶是否已認證
  const roles = to.meta.roles || [] // 路由所需的角色權限
  
  if (requiresAuth && !isAuthenticated) {
    // 未登錄,重定向到登錄頁
    next({ name: 'Login' })
  } else if (roles.length > 0 && !roles.some(role => userStore.hasRole(role))) {
    // 無權限,重定向到 403 頁面
    next({ name: 'Forbidden' })
  } else {
    // 已登錄或不需要認證,繼續(xù)導航
    next()
  }
}

4.2 路由模塊化

對于大型應用,可以將路由配置按照模塊進行拆分,提高代碼的可維護性。

// router/modules/auth.ts
// 認證模塊路由配置
// 設計意圖:將認證相關的路由配置拆分到獨立的模塊,提高代碼可維護性

import { AppRouteRecordRaw } from '../types'

/**
 * 認證模塊路由配置
 * 包含登錄、注冊等認證相關頁面的路由
 */
export const authRoutes: AppRouteRecordRaw[] = [
  {
    path: '/auth',
    component: () => import('@/layouts/AuthLayout.vue'), // 懶加載認證布局
    meta: {
      requiresAuth: false // 認證頁面不需要認證
    },
    children: [
      {
        path: 'login',
        name: 'Login',
        component: () => import('@/views/auth/login.vue'), // 懶加載登錄頁面
        meta: {
          title: '登錄'
        }
      },
      {
        path: 'register',
        name: 'Register',
        component: () => import('@/views/auth/register.vue'), // 懶加載注冊頁面
        meta: {
          title: '注冊'
        }
      }
    ]
  }
]

// router/modules/dashboard.ts
// 儀表盤模塊路由配置
// 設計意圖:將儀表盤相關的路由配置拆分到獨立的模塊,提高代碼可維護性

import { AppRouteRecordRaw } from '../types'

/**
 * 儀表盤模塊路由配置
 * 包含儀表盤頁面的路由
 */
export const dashboardRoutes: AppRouteRecordRaw[] = [
  {
    path: '/dashboard',
    component: () => import('@/layouts/DefaultLayout.vue'), // 懶加載默認布局
    meta: {
      requiresAuth: true // 需要認證
    },
    children: [
      {
        path: '',
        name: 'Dashboard',
        component: () => import('@/views/dashboard/index.vue'), // 懶加載儀表盤頁面
        meta: {
          title: '儀表盤',
          sidebar: true, // 在側邊欄顯示
          icon: 'dashboard' // 側邊欄圖標
        }
      }
    ]
  }
]

// router/routes.ts
// 路由配置主文件
// 設計意圖:組合所有路由模塊,形成完整的應用路由配置

import { AppRouteRecordRaw } from './types'
import { authRoutes } from './modules/auth' // 認證模塊路由
import { dashboardRoutes } from './modules/dashboard' // 儀表盤模塊路由
import { productRoutes } from './modules/product' // 產(chǎn)品模塊路由
import { userRoutes } from './modules/user' // 用戶模塊路由

/**
 * 路由配置數(shù)組
 * 組合所有路由模塊,形成完整的應用路由配置
 */
export const routes: AppRouteRecordRaw[] = [
  ...authRoutes, // 認證模塊路由
  ...dashboardRoutes, // 儀表盤模塊路由
  ...productRoutes, // 產(chǎn)品模塊路由
  ...userRoutes, // 用戶模塊路由
  {
    path: '/:pathMatch(.*)*',
    name: 'NotFound',
    component: () => import('@/views/error/404.vue'), // 懶加載 404 頁面
    meta: {
      title: '頁面不存在'
    }
  }
]

4.3 路由守衛(wèi)策略

路由守衛(wèi)是控制路由訪問權限的重要手段,合理使用路由守衛(wèi)能夠提高應用的安全性和用戶體驗。

全局守衛(wèi)

全局守衛(wèi)適用于所有路由,可以用于處理認證、權限檢查、頁面標題設置等通用邏輯。

// router/guards.ts
// 路由守衛(wèi)文件
// 設計意圖:實現(xiàn)路由的認證和權限控制,保護需要認證的路由

import { NavigationGuardNext, RouteLocationNormalized } from 'vue-router'
import { useUserStore } from '@/stores/user'

/**
 * 認證守衛(wèi)
 * 全局前置守衛(wèi),處理所有路由的認證和權限檢查
 * @param to 目標路由對象
 * @param from 來源路由對象
 * @param next 導航函數(shù),決定導航是否繼續(xù)
 */
export function authGuard(to: RouteLocationNormalized, from: RouteLocationNormalized, next: NavigationGuardNext) {
  const userStore = useUserStore() // 獲取用戶狀態(tài)管理 Store
  
  // 設置頁面標題
  if (to.meta.title) {
    document.title = `${to.meta.title} - My App`
  }
  
  // 權限檢查
  const requiresAuth = to.meta.requiresAuth !== false // 默認需要認證
  const isAuthenticated = userStore.isAuthenticated // 用戶是否已認證
  const roles = to.meta.roles || [] // 路由所需的角色權限
  
  if (requiresAuth && !isAuthenticated) {
    // 未登錄,重定向到登錄頁
    next({ name: 'Login' })
  } else if (roles.length > 0 && !roles.some(role => userStore.hasRole(role))) {
    // 無權限,重定向到 403 頁面
    next({ name: 'Forbidden' })
  } else {
    // 已登錄或不需要認證,繼續(xù)導航
    next()
  }
}

/**
 * 全局后置守衛(wèi)
 * 在路由導航完成后執(zhí)行,用于處理頁面加載完成后的邏輯
 * @param to 目標路由對象
 * @param from 來源路由對象
 */
export function globalAfterGuard(to: RouteLocationNormalized, from: RouteLocationNormalized) {
  // 頁面加載完成后的邏輯,如埋點統(tǒng)計、頁面性能監(jiān)控等
  console.log(`Navigated from ${from.path} to ${to.path}`)
}

路由獨享守衛(wèi)

路由獨享守衛(wèi)只適用于特定路由,可以用于處理該路由的特殊邏輯。

// router/routes.ts
// 路由定義文件
// 設計意圖:演示如何使用路由獨享守衛(wèi)處理特定路由的特殊邏輯

import { AppRouteRecordRaw } from './types'

/**
 * 路由配置數(shù)組
 */
export const routes: AppRouteRecordRaw[] = [
  {
    path: '/admin',
    name: 'Admin',
    component: () => import('@/views/admin/index.vue'), // 懶加載管理員頁面
    meta: {
      requiresAuth: true, // 需要認證
      roles: ['admin'] // 只有 admin 角色可以訪問
    },
    beforeEnter: (to, from, next) => {
      // 路由獨享守衛(wèi)邏輯
      // 只適用于當前路由,用于處理該路由的特殊邏輯
      console.log('Entering admin route')
      // 可以在這里添加額外的權限檢查或其他邏輯
      next() // 繼續(xù)導航
    }
  }
]

組件內守衛(wèi)

組件內守衛(wèi)適用于組件級別,可以用于處理組件的進入、離開等邏輯。

<script setup lang="ts">
// 組件內守衛(wèi)示例
// 設計意圖:演示如何在組件級別使用路由守衛(wèi),處理組件的進入、離開等邏輯

import { onBeforeRouteEnter, onBeforeRouteLeave, onBeforeRouteUpdate } from 'vue-router'

/**
 * 路由進入組件前
 * 在路由進入組件之前執(zhí)行,此時組件實例還未創(chuàng)建
 * @param to 目標路由對象
 * @param from 來源路由對象
 * @param next 導航函數(shù),決定導航是否繼續(xù)
 */
onBeforeRouteEnter((to, from, next) => {
  console.log('Before route enter')
  // 可以在這里進行數(shù)據(jù)預加載、權限檢查等
  // 例如:獲取組件需要的數(shù)據(jù),避免組件渲染時出現(xiàn)空白
  next() // 繼續(xù)導航
})

/**
 * 路由離開組件前
 * 在路由離開組件之前執(zhí)行,此時組件實例仍然存在
 * @param to 目標路由對象
 * @param from 來源路由對象
 * @param next 導航函數(shù),決定導航是否繼續(xù)
 */
onBeforeRouteLeave((to, from, next) => {
  console.log('Before route leave')
  // 可以在這里進行表單驗證、確認提示等
  // 例如:檢查用戶是否有未保存的修改,提示用戶確認離開
  next() // 繼續(xù)導航
})

/**
 * 路由更新但組件被復用時
 * 在路由更新但組件被復用時執(zhí)行,例如從 /user/1 導航到 /user/2
 * @param to 目標路由對象
 * @param from 來源路由對象
 * @param next 導航函數(shù),決定導航是否繼續(xù)
 */
onBeforeRouteUpdate((to, from, next) => {
  console.log('Before route update')
  // 可以在這里更新組件數(shù)據(jù)、重新獲取數(shù)據(jù)等
  // 例如:根據(jù)新的路由參數(shù)重新獲取用戶信息
  next() // 繼續(xù)導航
})
</script>

5. API 層設計

5.1 API 服務組織

一個合理的 API 層設計能夠清晰地管理與后端的通信,提高代碼的可維護性和可測試性。

API 服務文件結構

// services/api/index.ts
// API 客戶端配置文件
// 設計意圖:創(chuàng)建和配置 axios 實例,設置請求/響應攔截器,統(tǒng)一處理 API 調用

import axios from 'axios'
import { useUserStore } from '@/stores/user'

/**
 * 創(chuàng)建 axios 實例
 * 配置基礎 URL、超時時間、默認請求頭等
 */
const apiClient = axios.create({
  baseURL: import.meta.env.VITE_API_BASE_URL || '/api', // API 基礎 URL,從環(huán)境變量獲取或使用默認值
  timeout: 10000, // 請求超時時間,10秒
  headers: {
    'Content-Type': 'application/json' // 默認請求頭,使用 JSON 格式
  }
})

/**
 * 請求攔截器
 * 在發(fā)送請求之前執(zhí)行,用于添加認證令牌、請求日志等
 */
apiClient.interceptors.request.use(
  (config) => {
    const userStore = useUserStore() // 獲取用戶狀態(tài)管理 Store
    const token = userStore.token // 獲取認證令牌
    
    if (token) {
      // 如果有令牌,添加到請求頭的 Authorization 字段
      config.headers.Authorization = `Bearer ${token}`
    }
    
    return config // 返回修改后的配置
  },
  (error) => {
    // 請求錯誤處理
    return Promise.reject(error)
  }
)

/**
 * 響應攔截器
 * 在收到響應之后執(zhí)行,用于統(tǒng)一處理響應數(shù)據(jù)、錯誤處理等
 */
apiClient.interceptors.response.use(
  (response) => {
    // 只返回響應數(shù)據(jù),忽略其他響應信息
    return response.data
  },
  (error) => {
    // 錯誤處理
    if (error.response?.status === 401) {
      // 401 未授權錯誤,清除用戶登錄狀態(tài)
      const userStore = useUserStore()
      userStore.logout()
    }
    return Promise.reject(error) // 重新拋出錯誤,以便調用方處理
  }
)

export default apiClient

// services/api/user.ts
// 用戶 API 服務
// 設計意圖:封裝用戶相關的 API 調用,提供類型安全的 API 服務

import apiClient from './index'
import { User, LoginRequest, RegisterRequest } from '@/models/user'

/**
 * 用戶 API 服務
 * 封裝用戶相關的 API 調用,包括登錄、注冊、獲取用戶信息、更新用戶信息等
 */
export const userApi = {
  /**
   * 用戶登錄
   * @param data 登錄數(shù)據(jù),包含郵箱和密碼
   * @returns 登錄響應,包含用戶信息和認證令牌
   */
  login: (data: LoginRequest) => {
    return apiClient.post<{ user: User; token: string }>('/auth/login', data)
  },

  /**
   * 用戶注冊
   * @param data 注冊數(shù)據(jù),包含用戶基本信息
   * @returns 注冊響應,包含用戶信息和認證令牌
   */
  register: (data: RegisterRequest) => {
    return apiClient.post<{ user: User; token: string }>('/auth/register', data)
  },

  /**
   * 獲取當前用戶信息
   * @returns 當前用戶信息
   */
  getCurrentUser: () => {
    return apiClient.get<User>('/users/me')
  },

  /**
   * 更新用戶信息
   * @param data 用戶信息,部分更新
   * @returns 更新后的用戶信息
   */
  updateUser: (data: Partial<User>) => {
    return apiClient.put<User>('/users/me', data)
  }
}

// services/api/product.ts
// 產(chǎn)品 API 服務
// 設計意圖:封裝產(chǎn)品相關的 API 調用,提供類型安全的 API 服務

import apiClient from './index'
import { Product, CreateProductRequest, UpdateProductRequest } from '@/models/product'

/**
 * 產(chǎn)品 API 服務
 * 封裝產(chǎn)品相關的 API 調用,包括獲取產(chǎn)品列表、獲取產(chǎn)品詳情、創(chuàng)建產(chǎn)品、更新產(chǎn)品、刪除產(chǎn)品等
 */
export const productApi = {
  /**
   * 獲取產(chǎn)品列表
   * @param params 查詢參數(shù),包含分頁和篩選條件
   * @returns 產(chǎn)品列表
   */
  getProducts: (params?: { page?: number; limit?: number; category?: string }) => {
    return apiClient.get<Product[]>('/products', { params })
  },

  /**
   * 獲取產(chǎn)品詳情
   * @param id 產(chǎn)品 ID
   * @returns 產(chǎn)品詳情
   */
  getProduct: (id: number) => {
    return apiClient.get<Product>(`/products/${id}`)
  },

  /**
   * 創(chuàng)建產(chǎn)品
   * @param data 產(chǎn)品數(shù)據(jù),包含產(chǎn)品基本信息
   * @returns 創(chuàng)建的產(chǎn)品
   */
  createProduct: (data: CreateProductRequest) => {
    return apiClient.post<Product>('/products', data)
  },

  /**
   * 更新產(chǎn)品
   * @param id 產(chǎn)品 ID
   * @param data 產(chǎn)品數(shù)據(jù),部分更新
   * @returns 更新后的產(chǎn)品
   */
  updateProduct: (id: number, data: UpdateProductRequest) => {
    return apiClient.put<Product>(`/products/${id}`, data)
  },

  /**
   * 刪除產(chǎn)品
   * @param id 產(chǎn)品 ID
   * @returns 刪除結果
   */
  deleteProduct: (id: number) => {
    return apiClient.delete(`/products/${id}`)
  }
}

5.2 API 錯誤處理策略

合理的錯誤處理策略能夠提高應用的穩(wěn)定性和用戶體驗,以下是幾種常見的錯誤處理方式:

全局錯誤處理

通過 axios 響應攔截器統(tǒng)一處理 API 錯誤。

// services/api/index.ts
// API 客戶端配置文件
// 設計意圖:通過響應攔截器實現(xiàn)全局錯誤處理,統(tǒng)一處理不同類型的 API 錯誤

import axios from 'axios'
import { useUserStore } from '@/stores/user'
import { ElMessage } from 'element-plus'

/**
 * 響應攔截器
 * 統(tǒng)一處理 API 錯誤,根據(jù)錯誤狀態(tài)碼顯示不同的錯誤提示
 */
apiClient.interceptors.response.use(
  (response) => {
    return response.data // 只返回響應數(shù)據(jù)
  },
  (error) => {
    // 處理網(wǎng)絡錯誤(無響應)
    if (!error.response) {
      ElMessage.error('網(wǎng)絡連接失敗,請檢查網(wǎng)絡設置')
      return Promise.reject(error)
    }

    // 處理 401 錯誤(未授權)
    if (error.response.status === 401) {
      const userStore = useUserStore()
      userStore.logout() // 清除用戶登錄狀態(tài)
      ElMessage.error('登錄已過期,請重新登錄')
      return Promise.reject(error)
    }

    // 處理 403 錯誤(禁止訪問)
    if (error.response.status === 403) {
      ElMessage.error('沒有權限訪問該資源')
      return Promise.reject(error)
    }

    // 處理 404 錯誤(資源不存在)
    if (error.response.status === 404) {
      ElMessage.error('請求的資源不存在')
      return Promise.reject(error)
    }

    // 處理 500 錯誤(服務器錯誤)
    if (error.response.status >= 500) {
      ElMessage.error('服務器錯誤,請稍后重試')
      return Promise.reject(error)
    }

    // 處理其他錯誤
    const errorMessage = error.response.data?.message || '請求失敗,請稍后重試'
    ElMessage.error(errorMessage)
    return Promise.reject(error)
  }
)

局部錯誤處理

在具體的 API 調用處進行錯誤處理,適用于需要特殊處理的場景。

// composables/useAuth.ts
// 認證相關邏輯
// 設計意圖:在具體的 API 調用處進行錯誤處理,適用于需要特殊處理的場景

import { ref } from 'vue'
import { userApi } from '@/services/api/user'
import { LoginRequest, RegisterRequest } from '@/models/user'

/**
 * 認證相關邏輯
 * 封裝登錄、注冊等認證操作,并在具體的 API 調用處進行錯誤處理
 */
export function useAuth() {
  const loading = ref(false) // 加載狀態(tài)
  const error = ref<string | null>(null) // 錯誤信息

  /**
   * 用戶登錄
   * 在具體的 API 調用處進行錯誤處理,提供更具體的錯誤提示
   * @param email 郵箱
   * @param password 密碼
   * @returns 登錄響應
   * @throws 登錄失敗時拋出錯誤
   */
  async function login(email: string, password: string) {
    loading.value = true // 開始加載
    error.value = null // 清空錯誤信息
    try {
      const response = await userApi.login({ email, password }) // 調用登錄 API
      return response // 返回登錄響應
    } catch (err: any) {
      // 自定義錯誤信息,提供更具體的錯誤提示
      error.value = err.response?.data?.message || '登錄失敗,請檢查郵箱和密碼'
      throw err // 重新拋出錯誤,以便調用方處理
    } finally {
      loading.value = false // 結束加載
    }
  }

  /**
   * 用戶注冊
   * 在具體的 API 調用處進行錯誤處理,提供更具體的錯誤提示
   * @param userData 注冊數(shù)據(jù)
   * @returns 注冊響應
   * @throws 注冊失敗時拋出錯誤
   */
  async function register(userData: RegisterRequest) {
    loading.value = true // 開始加載
    error.value = null // 清空錯誤信息
    try {
      const response = await userApi.register(userData) // 調用注冊 API
      return response // 返回注冊響應
    } catch (err: any) {
      // 自定義錯誤信息,提供更具體的錯誤提示
      error.value = err.response?.data?.message || '注冊失敗,請稍后重試'
      throw err // 重新拋出錯誤,以便調用方處理
    } finally {
      loading.value = false // 結束加載
    }
  }

  return {
    loading, // 加載狀態(tài)
    error, // 錯誤信息
    login, // 登錄方法
    register // 注冊方法
  }
}

5.3 API 數(shù)據(jù)緩存策略

合理的 API 數(shù)據(jù)緩存策略能夠減少重復請求,提高應用的性能和用戶體驗。

內存緩存

使用內存緩存存儲臨時數(shù)據(jù),適用于單次會話中的數(shù)據(jù)。

// services/api/product.ts
// 產(chǎn)品 API 服務
// 設計意圖:通過內存緩存減少重復請求,提高應用性能和用戶體驗

import apiClient from './index'
import { Product } from '@/models/product'

// 產(chǎn)品列表緩存
// 使用內存變量存儲產(chǎn)品列表數(shù)據(jù)和緩存時間戳
let productsCache: { data: Product[]; timestamp: number } | null = null
const CACHE_DURATION = 5 * 60 * 1000 // 緩存持續(xù)時間,5 分鐘

/**
 * 產(chǎn)品 API 服務
 */
export const productApi = {
  /**
   * 獲取產(chǎn)品列表
   * 使用內存緩存減少重復請求,提高應用性能
   * @param params 查詢參數(shù),包含分頁和篩選條件
   * @returns 產(chǎn)品列表
   */
  getProducts: async (params?: { page?: number; limit?: number; category?: string }) => {
    // 檢查緩存是否有效
    // 如果緩存存在且未過期,直接返回緩存數(shù)據(jù)
    if (productsCache && Date.now() - productsCache.timestamp < CACHE_DURATION) {
      return productsCache.data
    }

    // 緩存無效或不存在,發(fā)起 API 請求
    const data = await apiClient.get<Product[]>('/products', { params })
    
    // 更新緩存
    // 存儲響應數(shù)據(jù)和當前時間戳
    productsCache = {
      data,
      timestamp: Date.now()
    }

    return data
  },

  /**
   * 清除產(chǎn)品列表緩存
   * 在產(chǎn)品數(shù)據(jù)發(fā)生變化時調用,確保下次獲取最新數(shù)據(jù)
   */
  clearProductsCache: () => {
    productsCache = null
  }
}

本地存儲緩存

使用 localStorage 或 sessionStorage 存儲持久化數(shù)據(jù),適用于需要跨會話保持的數(shù)據(jù)。

// services/api/user.ts
// 用戶 API 服務
// 設計意圖:通過本地存儲緩存實現(xiàn)數(shù)據(jù)持久化,適用于需要跨會話保持的數(shù)據(jù)

import apiClient from './index'
import { User } from '@/models/user'

const USER_CACHE_KEY = 'user_cache' // 本地存儲的緩存鍵名
const CACHE_DURATION = 24 * 60 * 60 * 1000 // 緩存持續(xù)時間,24 小時

/**
 * 用戶 API 服務
 */
export const userApi = {
  /**
   * 獲取當前用戶信息
   * 使用本地存儲緩存實現(xiàn)數(shù)據(jù)持久化,減少重復請求
   * @returns 用戶信息
   */
  getCurrentUser: async () => {
    // 檢查緩存
    const cachedUser = localStorage.getItem(USER_CACHE_KEY)
    if (cachedUser) {
      const { data, timestamp } = JSON.parse(cachedUser)
      // 如果緩存存在且未過期,直接返回緩存數(shù)據(jù)
      if (Date.now() - timestamp < CACHE_DURATION) {
        return data
      }
    }

    // 緩存無效或不存在,發(fā)起 API 請求
    const data = await apiClient.get<User>('/users/me')
    
    // 更新緩存
    // 將響應數(shù)據(jù)和當前時間戳存儲到本地存儲
    localStorage.setItem(USER_CACHE_KEY, JSON.stringify({
      data,
      timestamp: Date.now()
    }))

    return data
  },

  /**
   * 清除用戶緩存
   * 在用戶信息發(fā)生變化時調用,確保下次獲取最新數(shù)據(jù)
   */
  clearUserCache: () => {
    localStorage.removeItem(USER_CACHE_KEY)
  }
}

6. 工具類和公共組件

6.1 工具函數(shù)組織

工具函數(shù)是項目中常用的輔助函數(shù),合理組織工具函數(shù)能夠提高代碼的復用性和可維護性。

工具函數(shù)目錄結構

src/utils/
├── format.ts          # 格式化工具
├── validation.ts      # 驗證工具
├── storage.ts         # 存儲工具
├── http.ts            # HTTP 工具
├── date.ts            # 日期工具
├── number.ts          # 數(shù)字工具
├── string.ts          # 字符串工具
├── array.ts           # 數(shù)組工具
└── object.ts          # 對象工具

工具函數(shù)示例

// utils/format.ts
// 格式化工具函數(shù)
// 設計意圖:提供常用的格式化功能,如金額格式化、日期格式化等

/**
 * 格式化金額
 * 將數(shù)字金額格式化為帶有貨幣符號和千分位的字符串
 * @param amount 金額,數(shù)字類型
 * @param currency 貨幣符號,默認為 ¥
 * @returns 格式化后的金額字符串,如 "¥1,234.56"
 */
export function formatCurrency(amount: number, currency: string = '¥'): string {
  // 使用 toFixed(2) 保留兩位小數(shù),然后使用正則表達式添加千分位
  return `${currency}${amount.toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ',')}`
}

/**
 * 格式化日期
 * 將日期對象、字符串或時間戳格式化為指定格式的日期字符串
 * @param date 日期,可以是 Date 對象、日期字符串或時間戳
 * @param format 格式化模板,默認為 'YYYY-MM-DD'
 * @returns 格式化后的日期字符串,如 "2023-12-25"
 */
export function formatDate(date: Date | string | number, format: string = 'YYYY-MM-DD'): string {
  const d = new Date(date) // 創(chuàng)建 Date 對象
  const year = d.getFullYear() // 獲取年份
  const month = String(d.getMonth() + 1).padStart(2, '0') // 獲取月份(0-11,所以加 1),并補零
  const day = String(d.getDate()).padStart(2, '0') // 獲取日期,并補零
  const hours = String(d.getHours()).padStart(2, '0') // 獲取小時,并補零
  const minutes = String(d.getMinutes()).padStart(2, '0') // 獲取分鐘,并補零
  const seconds = String(d.getSeconds()).padStart(2, '0') // 獲取秒數(shù),并補零

  // 替換格式化模板中的占位符
  return format
    .replace('YYYY', String(year))
    .replace('MM', month)
    .replace('DD', day)
    .replace('HH', hours)
    .replace('mm', minutes)
    .replace('ss', seconds)
}

// utils/validation.ts
// 驗證工具函數(shù)
// 設計意圖:提供常用的驗證功能,如郵箱驗證、密碼強度驗證等

/**
 * 驗證郵箱
 * 檢查郵箱地址是否符合標準格式
 * @param email 郵箱地址,字符串類型
 * @returns 是否為有效郵箱,布爾值
 */
export function isValidEmail(email: string): boolean {
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/ // 簡單的郵箱格式正則表達式
  return emailRegex.test(email) // 測試郵箱地址是否匹配正則表達式
}

/**
 * 驗證密碼強度
 * 根據(jù)密碼長度、包含的字符類型等計算密碼強度等級
 * @param password 密碼,字符串類型
 * @returns 密碼強度等級 (0-4),數(shù)字越大表示密碼強度越高
 */
export function getPasswordStrength(password: string): number {
  let strength = 0 // 初始強度為 0
  if (password.length >= 8) strength++ // 長度至少 8 位
  if (/[A-Z]/.test(password)) strength++ // 包含大寫字母
  if (/[a-z]/.test(password)) strength++ // 包含小寫字母
  if (/[0-9]/.test(password)) strength++ // 包含數(shù)字
  if (/[^A-Za-z0-9]/.test(password)) strength++ // 包含特殊字符
  return strength // 返回密碼強度等級
}

// utils/storage.ts
// 存儲工具函數(shù)
// 設計意圖:封裝本地存儲操作,提供類型安全的存儲工具函數(shù)

/**
 * 設置本地存儲
 * 將值存儲到 localStorage 中,自動進行 JSON 序列化
 * @param key 存儲鍵,字符串類型
 * @param value 存儲值,可以是任何可 JSON 序列化的類型
 */
export function setLocalStorage(key: string, value: any): void {
  try {
    // 將值轉換為 JSON 字符串并存儲
    localStorage.setItem(key, JSON.stringify(value))
  } catch (error) {
    // 捕獲并記錄錯誤,避免存儲失敗導致應用崩潰
    console.error('Error setting localStorage:', error)
  }
}

/**
 * 獲取本地存儲
 * 從 localStorage 中獲取值,自動進行 JSON 反序列化
 * @param key 存儲鍵,字符串類型
 * @param defaultValue 默認值,當存儲不存在或解析失敗時返回
 * @returns 存儲值或默認值,類型為 T
 */
export function getLocalStorage<T>(key: string, defaultValue: T): T {
  try {
    const value = localStorage.getItem(key) // 獲取存儲的 JSON 字符串
    return value ? JSON.parse(value) : defaultValue // 解析 JSON 字符串,失敗則返回默認值
  } catch (error) {
    // 捕獲并記錄錯誤,返回默認值
    console.error('Error getting localStorage:', error)
    return defaultValue
  }
}

/**
 * 刪除本地存儲
 * 從 localStorage 中刪除指定的存儲項
 * @param key 存儲鍵,字符串類型
 */
export function removeLocalStorage(key: string): void {
  try {
    localStorage.removeItem(key) // 刪除存儲項
  } catch (error) {
    // 捕獲并記錄錯誤
    console.error('Error removing localStorage:', error)
  }
}

6.2 公共組件組織

公共組件是項目中可復用的 UI 組件,合理組織公共組件能夠提高開發(fā)效率和 UI 一致性。

公共組件目錄結構

src/components/
├── common/            # 通用基礎組件
│   ├── Button.vue     # 按鈕組件
│   ├── Input.vue      # 輸入框組件
│   ├── Dialog.vue     # 對話框組件
│   └── Loading.vue    # 加載組件
├── layout/            # 布局相關組件
│   ├── Header.vue     # 頭部組件
│   ├── Sidebar.vue    # 側邊欄組件
│   └── Footer.vue     # 底部組件
└── business/          # 業(yè)務相關組件
    ├── UserCard.vue   # 用戶卡片組件
    ├── ProductList.vue # 產(chǎn)品列表組件
    └── OrderForm.vue  # 訂單表單組件

公共組件設計原則

  1. 單一職責:每個組件只負責一個功能,便于理解和維護
  2. 可配置性:通過 props 提供足夠的配置選項,提高組件的靈活性
  3. 可擴展性:設計合理的組件結構,便于后續(xù)功能的擴展
  4. 類型安全:充分利用 TypeScript 的類型系統(tǒng),為組件定義明確的類型
  5. 文檔完備:為組件提供詳細的文檔和使用示例

公共組件示例

<!-- components/common/Button.vue -->
<!-- 通用按鈕組件 -->
<!-- 設計意圖:提供一個可配置的通用按鈕組件,支持多種類型、尺寸和狀態(tài) -->

<template>
  <button
    :class="[
      'btn',
      `btn-${variant}`, // 按鈕類型樣式
      `btn-${size}`, // 按鈕尺寸樣式
      { 'btn-block': block }, // 塊級按鈕樣式
      { 'btn-disabled': disabled } // 禁用狀態(tài)樣式
    ]"
    :disabled="disabled" // 禁用狀態(tài)
    @click="$emit('click', $event)" // 點擊事件
  >
    <slot></slot> <!-- 按鈕內容插槽 -->
  </button>
</template>

<script setup lang="ts">
// 使用 Composition API 語法
import { defineProps, defineEmits } from 'vue'

/**
 * 按鈕類型
 * 支持多種預設的按鈕樣式
 */
export type ButtonVariant = 'primary' | 'secondary' | 'success' | 'danger' | 'warning' | 'info'

/**
 * 按鈕尺寸
 * 支持多種預設的按鈕尺寸
 */
export type ButtonSize = 'sm' | 'md' | 'lg'

/**
 * 按鈕組件屬性
 * 使用 TypeScript 類型定義,提供更好的類型提示
 */
const props = defineProps<{
  /**
   * 按鈕類型
   * 決定按鈕的顏色樣式
   * @default 'primary'
   */
  variant?: ButtonVariant
  /**
   * 按鈕尺寸
   * 決定按鈕的大小
   * @default 'md'
   */
  size?: ButtonSize
  /**
   * 是否為塊級按鈕
   * 塊級按鈕會占滿父容器的寬度
   * @default false
   */
  block?: boolean
  /**
   * 是否禁用
   * 禁用狀態(tài)下按鈕不可點擊
   * @default false
   */
  disabled?: boolean
}>()

/**
 * 按鈕組件事件
 * 使用 TypeScript 類型定義,提供更好的類型提示
 */
const emit = defineEmits<{
  /**
   * 點擊事件
   * 當用戶點擊按鈕時觸發(fā)
   * @param event 點擊事件對象
   */
  (e: 'click', event: MouseEvent): void
}>()
</script>

<style scoped>
/* 按鈕基礎樣式 */
.btn {
  display: inline-block;
  padding: 0.5rem 1rem;
  border: none;
  border-radius: 0.25rem;
  font-weight: 500;
  cursor: pointer;
  transition: all 0.2s ease;
}

/* 按鈕類型樣式 */
.btn-primary {
  background-color: #3b82f6;
  color: white;
}

.btn-secondary {
  background-color: #6b7280;
  color: white;
}

.btn-success {
  background-color: #10b981;
  color: white;
}

.btn-danger {
  background-color: #ef4444;
  color: white;
}

.btn-warning {
  background-color: #f59e0b;
  color: white;
}

.btn-info {
  background-color: #3b82f6;
  color: white;
}

/* 按鈕尺寸樣式 */
.btn-sm {
  padding: 0.25rem 0.5rem;
  font-size: 0.875rem;
}

.btn-md {
  padding: 0.5rem 1rem;
  font-size: 1rem;
}

.btn-lg {
  padding: 0.75rem 1.5rem;
  font-size: 1.125rem;
}

/* 塊級按鈕樣式 */
.btn-block {
  display: block;
  width: 100%;
}

/* 禁用狀態(tài)樣式 */
.btn-disabled {
  opacity: 0.6;
  cursor: not-allowed;
}
</style>

6.3 組合式 API 邏輯組織

組合式 API 是 Vue 3 的重要特性,合理組織組合式 API 邏輯能夠提高代碼的復用性和可維護性。

組合式 API 目錄結構

src/composables/
├── useAuth.ts         # 認證相關邏輯
├── useApi.ts          # API 調用邏輯
├── useLocalStorage.ts # 本地存儲邏輯
├── useValidation.ts   # 驗證相關邏輯
├── useDebounce.ts     # 防抖邏輯
└── useThrottle.ts     # 節(jié)流邏輯

組合式 API 示例

// composables/useAuth.ts
// 認證相關邏輯
// 設計意圖:封裝認證相關的邏輯,供多個組件復用,實現(xiàn)邏輯的模塊化和可維護性

import { ref, computed } from 'vue'
import { userApi } from '@/services/api/user'
import { useUserStore } from '@/stores/user'
import { LoginRequest, RegisterRequest } from '@/models/user'

/**
 * 認證相關邏輯
 * 封裝登錄、注冊、登出等認證操作,提供統(tǒng)一的認證狀態(tài)管理
 * 使用 Composition API 風格,便于在組件中使用
 * @returns 認證相關的狀態(tài)和方法
 */
export function useAuth() {
  const userStore = useUserStore() // 獲取用戶狀態(tài)管理 Store
  const loading = ref(false) // 加載狀態(tài)
  const error = ref<string | null>(null) // 錯誤信息

  /**
   * 是否已認證
   * 從用戶 Store 中獲取認證狀態(tài)
   */
  const isAuthenticated = computed(() => userStore.isAuthenticated)

  /**
   * 當前用戶
   * 從用戶 Store 中獲取當前用戶信息
   */
  const currentUser = computed(() => userStore.currentUser)

  /**
   * 用戶登錄
   * 調用登錄 API,更新用戶狀態(tài)
   * @param email 郵箱
   * @param password 密碼
   * @returns 登錄響應
   * @throws 登錄失敗時拋出錯誤
   */
  async function login(email: string, password: string) {
    loading.value = true // 開始加載
    error.value = null // 清空錯誤信息
    try {
      const response = await userApi.login({ email, password }) // 調用登錄 API
      userStore.login(response.user, response.token) // 更新用戶狀態(tài)
      return response // 返回登錄響應
    } catch (err: any) {
      // 自定義錯誤信息
      error.value = err.response?.data?.message || '登錄失敗,請檢查郵箱和密碼'
      throw err // 重新拋出錯誤
    } finally {
      loading.value = false // 結束加載
    }
  }

  /**
   * 用戶注冊
   * 調用注冊 API,更新用戶狀態(tài)
   * @param userData 用戶注冊數(shù)據(jù)
   * @returns 注冊響應
   * @throws 注冊失敗時拋出錯誤
   */
  async function register(userData: RegisterRequest) {
    loading.value = true // 開始加載
    error.value = null // 清空錯誤信息
    try {
      const response = await userApi.register(userData) // 調用注冊 API
      userStore.login(response.user, response.token) // 更新用戶狀態(tài)
      return response // 返回注冊響應
    } catch (err: any) {
      // 自定義錯誤信息
      error.value = err.response?.data?.message || '注冊失敗,請稍后重試'
      throw err // 重新拋出錯誤
    } finally {
      loading.value = false // 結束加載
    }
  }

  /**
   * 用戶登出
   * 調用登出 API,清除用戶狀態(tài)
   * @throws 登出失敗時拋出錯誤
   */
  async function logout() {
    loading.value = true // 開始加載
    error.value = null // 清空錯誤信息
    try {
      await userApi.logout() // 調用登出 API
      userStore.logout() // 清除用戶狀態(tài)
    } catch (err: any) {
      // 自定義錯誤信息
      error.value = err.response?.data?.message || '登出失敗,請稍后重試'
      throw err // 重新拋出錯誤
    } finally {
      loading.value = false // 結束加載
    }
  }

  /**
   * 獲取當前用戶信息
   * 調用獲取用戶信息 API,更新用戶狀態(tài)
   * @returns 用戶信息
   * @throws 獲取失敗時拋出錯誤
   */
  async function fetchCurrentUser() {
    loading.value = true // 開始加載
    error.value = null // 清空錯誤信息
    try {
      const user = await userApi.getCurrentUser() // 調用獲取用戶信息 API
      userStore.setCurrentUser(user) // 更新用戶狀態(tài)
      return user // 返回用戶信息
    } catch (err: any) {
      // 自定義錯誤信息
      error.value = err.response?.data?.message || '獲取用戶信息失敗'
      throw err // 重新拋出錯誤
    } finally {
      loading.value = false // 結束加載
    }
  }

  return {
    loading, // 加載狀態(tài)
    error, // 錯誤信息
    isAuthenticated, // 是否已認證
    currentUser, // 當前用戶
    login, // 登錄方法
    register, // 注冊方法
    logout, // 登出方法
    fetchCurrentUser // 獲取當前用戶信息方法
  }
}

// composables/useApi.ts
// API 調用邏輯
// 設計意圖:封裝 API 請求的通用邏輯,供多個組件和 composables 復用,實現(xiàn)錯誤處理和加載狀態(tài)的統(tǒng)一管理

import { ref } from 'vue'

/**
 * API 調用邏輯
 * 封裝 API 請求的通用邏輯,包括加載狀態(tài)管理和錯誤處理
 * 使用 Composition API 風格,便于在組件和其他 composables 中使用
 * @returns API 調用相關的狀態(tài)和方法
 */
export function useApi() {
  const loading = ref(false) // 加載狀態(tài)
  const error = ref<string | null>(null) // 錯誤信息

  /**
   * 發(fā)起 API 請求
   * 封裝 API 請求的通用邏輯,處理加載狀態(tài)和錯誤
   * @param apiCall API 調用函數(shù),返回 Promise
   * @returns API 響應數(shù)據(jù)
   * @throws API 請求失敗時拋出錯誤
   */
  async function request<T>(apiCall: () => Promise<T>): Promise<T> {
    loading.value = true // 開始加載
    error.value = null // 清空錯誤信息
    try {
      return await apiCall() // 執(zhí)行 API 調用
    } catch (err: any) {
      // 自定義錯誤信息
      error.value = err.response?.data?.message || '請求失敗,請稍后重試'
      throw err // 重新拋出錯誤
    } finally {
      loading.value = false // 結束加載
    }
  }

  return {
    loading, // 加載狀態(tài)
    error, // 錯誤信息
    request // API 請求方法
  }
}

7. 企業(yè)級項目案例分析

7.1 項目架構演進

初始架構

在項目初期,通常會采用相對簡單的架構,以快速實現(xiàn)核心功能。

// 初始架構
// 項目初期的簡單架構,以快速實現(xiàn)核心功能為目標
// 設計意圖:在項目初期,使用簡單的目錄結構,快速搭建項目框架

// 特點:
// - 目錄結構簡單,易于理解和快速上手
// - 功能模塊劃分明確,便于初期開發(fā)
// - 適用于中小型項目或項目初期階段

// 適用場景:項目初期,功能相對簡單,團隊規(guī)模較小
src/
├── assets/            # 靜態(tài)資源(圖片、樣式等)
├── components/        # 公共組件
├── views/             # 頁面組件
├── router/            # 路由配置
├── store/             # 狀態(tài)管理(如 Vuex)
├── services/          # API 服務
├── utils/             # 工具函數(shù)
├── App.vue            # 根組件
└── main.ts            # 應用入口文件

演進架構

隨著項目規(guī)模的擴大,架構會逐漸演進,添加更多的功能模塊和組織結構。

// 演進架構
// 隨著項目規(guī)模擴大,架構逐漸演進,添加更多功能模塊和組織結構
// 設計意圖:通過完善的目錄結構,提高代碼的可維護性和可擴展性,適應項目的不斷發(fā)展

// 特點:
// - 目錄結構更加完善,功能模塊劃分更加細致
// - 引入了更多的 TypeScript 相關目錄,如 types、enums 等
// - 使用 Pinia 替代 Vuex,采用 Composition API 風格
// - 增加了更多的功能模塊,如 composables、layouts、models 等

// 適用場景:中大型項目,功能相對復雜,團隊規(guī)模較大
src/
├── assets/            # 靜態(tài)資源(圖片、樣式、圖標等)
├── components/        # 公共組件(按功能或類型劃分)
├── composables/       # 組合式 API 邏輯(可復用的業(yè)務邏輯)
├── constants/         # 常量定義(API 地址、路由名稱等)
├── enums/             # 枚舉類型(用戶角色、訂單狀態(tài)等)
├── layouts/           # 布局組件(默認布局、認證布局等)
├── models/            # 數(shù)據(jù)模型(用戶模型、產(chǎn)品模型等)
├── router/            # 路由配置(路由定義、守衛(wèi)等)
├── services/          # 服務層(API 服務、工具服務等)
├── stores/            # Pinia 狀態(tài)管理(按模塊劃分的狀態(tài)管理)
├── types/             # TypeScript 類型定義(API 類型、組件類型等)
├── utils/             # 工具函數(shù)(格式化、驗證、存儲等)
├── views/             # 頁面組件(按業(yè)務功能劃分)
├── App.vue            # 根組件
├── main.ts            # 應用入口文件
└── env.d.ts           # 環(huán)境變量類型聲明

微前端架構

對于超大型應用,可以考慮采用微前端架構,將應用拆分為多個獨立的微應用。

// 微前端架構
// 對于超大型應用,采用微前端架構,將應用拆分為多個獨立的微應用
// 設計意圖:通過微前端架構,實現(xiàn)應用的模塊化和獨立部署,提高開發(fā)效率和團隊協(xié)作

// 特點:
// - 將應用拆分為多個獨立的微應用,每個微應用可以獨立開發(fā)、部署和維護
// - 主應用(shell)負責微應用的加載和管理
// - 共享資源(packages)供多個微應用復用
// - 提高了應用的可擴展性和可維護性

// 適用場景:超大型應用,多個團隊負責不同業(yè)務模塊,需要獨立部署和維護
apps/                  # 應用目錄,包含所有微應用
├── shell/             # 主應用(shell),負責微應用的加載和管理
├── auth/              # 認證微應用,處理用戶登錄、注冊等認證功能
├── dashboard/         # 儀表盤微應用,展示用戶儀表盤和概覽數(shù)據(jù)
└── products/          # 產(chǎn)品微應用,處理產(chǎn)品相關的功能

packages/              # 共享資源目錄,包含多個微應用共用的代碼
├── components/        # 共享組件,供多個微應用復用
├── utils/             # 共享工具函數(shù),供多個微應用復用
└── types/             # 共享類型定義,供多個微應用復用

7.2 架構優(yōu)化策略

  1. 性能優(yōu)化

    • 代碼分割和懶加載
    • 資源壓縮和緩存
    • 減少不必要的重渲染
  2. 可維護性優(yōu)化

    • 模塊化和組件化
    • 代碼規(guī)范和命名約定
    • 文檔和注釋
  3. 可擴展性優(yōu)化

    • 插件化架構
    • 配置驅動開發(fā)
    • 依賴注入
  4. 安全性優(yōu)化

    • 認證和授權
    • 數(shù)據(jù)驗證和 sanitization
    • 防止 XSS 和 CSRF 攻擊

7.3 實際項目案例

電商平臺

架構特點

  • 模塊化設計,按業(yè)務功能劃分模塊
  • 微前端架構,將不同業(yè)務域拆分為獨立的微應用
  • 服務端渲染,提高首屏加載速度和 SEO
  • 實時數(shù)據(jù)更新,使用 WebSocket 實現(xiàn)商品價格和庫存的實時更新

核心模塊

  • 用戶認證模塊:處理用戶登錄、注冊、密碼重置等
  • 商品模塊:處理商品列表、詳情、搜索等
  • 購物車模塊:處理購物車添加、修改、結算等
  • 訂單模塊:處理訂單創(chuàng)建、支付、物流等
  • 支付模塊:集成多種支付方式

企業(yè)管理系統(tǒng)

架構特點

  • 權限管理系統(tǒng),基于角色的訪問控制
  • 工作流引擎,支持自定義業(yè)務流程
  • 數(shù)據(jù)可視化,提供豐富的報表和圖表
  • 多語言支持,適應國際化需求

核心模塊

  • 用戶管理模塊:處理用戶信息、角色、權限等
  • 組織管理模塊:處理部門、職位、員工等
  • 資產(chǎn)管理模塊:處理資產(chǎn)登記、折舊、盤點等
  • 財務管理模塊:處理預算、報銷、審批等
  • 項目管理模塊:處理項目計劃、任務、進度等

8. 總結與實踐

8.1 項目架構實踐

  1. 合理的目錄結構

    • 按照功能模塊組織代碼
    • 建立清晰的層次結構
    • 采用一致的命名規(guī)范
  2. 狀態(tài)管理策略

    • 使用 Pinia 進行狀態(tài)管理
    • 按模塊劃分 Store
    • 合理使用持久化存儲
  3. 路由架構設計

    • 模塊化路由配置
    • 合理使用路由守衛(wèi)
    • 支持動態(tài)路由和權限控制
  4. API 層設計

    • 統(tǒng)一的 API 服務封裝
    • 合理的錯誤處理策略
    • 有效的數(shù)據(jù)緩存機制
  5. 工具類和公共組件

    • 可復用的工具函數(shù)
    • 可配置的公共組件
    • 類型安全的組合式 API
  6. 代碼質量保證

    • 嚴格的 TypeScript 類型檢查
    • 統(tǒng)一的代碼規(guī)范和格式化
    • 完善的測試用例

8.2 團隊協(xié)作實踐

  1. 代碼規(guī)范

    • 制定統(tǒng)一的代碼規(guī)范
    • 使用 ESLint 和 Prettier 進行代碼檢查和格式化
    • 定期進行代碼審查
  2. 版本控制

    • 采用 Git 工作流
    • 合理的分支管理策略
    • 規(guī)范的提交信息
  3. 文檔管理

    • 項目架構文檔
    • 組件和 API 文檔
    • 開發(fā)和部署指南
  4. 自動化工具

    • CI/CD 流程
    • 自動化測試
    • 代碼質量檢測

8.3 未來發(fā)展趨勢

  1. 微前端架構

    • 將大型應用拆分為多個獨立的微應用
    • 提高團隊協(xié)作效率和應用可維護性
  2. Serverless 架構

    • 前端和后端都采用 Serverless 架構
    • 降低運維成本和提高擴展性
  3. AI 輔助開發(fā)

    • 使用 AI 工具輔助代碼生成和優(yōu)化
    • 提高開發(fā)效率和代碼質量
  4. WebAssembly

    • 部分計算密集型任務使用 WebAssembly
    • 提高應用性能
  5. 邊緣計算

    • 將部分計算和存儲遷移到邊緣節(jié)點
    • 減少延遲和提高用戶體驗

9. 附錄

9.1 技術棧推薦

| 類別 | 技術 | 版本 | 用途 |

總結

Vue 3 + TypeScript 項目架構設計是一個持續(xù)迭代和優(yōu)化的過程,需要根據(jù)項目的具體需求和團隊的實際情況進行靈活調整和優(yōu)化。通過合理的架構設計和實踐,我們可以構建出高質量、可維護、可擴展的 Vue 3 + TypeScript 項目,為前端開發(fā)團隊帶來更好的開發(fā)體驗和更高的生產(chǎn)效率。

到此這篇關于Vue3+TypeScript項目架構實踐指南的文章就介紹到這了,更多相關Vue3+TS項目架構內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • 一文教會你搭建vite項目并配置路由和element-plus

    一文教會你搭建vite項目并配置路由和element-plus

    由于項目搭建過程實在繁瑣,容易遺忘,每次新建項目還得百度一下怎么搭建,所以寫下本文提醒自己,下面這篇文章主要給大家介紹了關于搭建vite項目并配置路由和element-plus的相關資料,需要的朋友可以參考下
    2022-07-07
  • 在vue項目實現(xiàn)一個ctrl+f的搜索功能

    在vue項目實現(xiàn)一個ctrl+f的搜索功能

    剛剛接到領導通知,需要實現(xiàn)搜索功能,因為項目是vue的而且是手機端,對我來說有點小難度。經(jīng)過小編的一番思索最終還是解決了,今天小編把實現(xiàn)過程分享到腳本之家平臺,需要的朋友參考下
    2020-02-02
  • vue2項目使用exceljs多表頭導出功能詳解

    vue2項目使用exceljs多表頭導出功能詳解

    ExcelJS是一個用于在Node.js和瀏覽器中創(chuàng)建、讀取和修改Excel文件的強大JavaScript庫,下面這篇文章主要給大家介紹了關于vue2項目使用exceljs多表頭導出功能的相關資料,需要的朋友可以參考下
    2024-05-05
  • Vue嵌套iframe時$router.go(-1)后退bug的原因解析

    Vue嵌套iframe時$router.go(-1)后退bug的原因解析

    這篇文章主要介紹了Vue嵌套iframe,$router.go(-1)后退bug的問題原因及解決方法,本文給大家分享問題原因所在及解決方案,需要的朋友可以參考下吧
    2023-09-09
  • el-menu遞歸實現(xiàn)多級菜單組件的示例

    el-menu遞歸實現(xiàn)多級菜單組件的示例

    本文主要介紹了el-menu使用遞歸組件實現(xiàn)多級菜單組件,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2023-04-04
  • web項目開發(fā)VUE的混入與繼承原理

    web項目開發(fā)VUE的混入與繼承原理

    這篇文章主要介紹了web項目開發(fā)中VUE的混入與繼承原理,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2021-09-09
  • 詳細講解如何創(chuàng)建, 發(fā)布自己的 Vue UI 組件庫

    詳細講解如何創(chuàng)建, 發(fā)布自己的 Vue UI 組件庫

    當我們自己開發(fā)了一個 _UI Component_, 需要在多個項目中使用的時候呢? 我們首先想到的可能是直接復制一份過去對嗎?我們?yōu)槭裁床话l(fā)布一個 UI 組件庫給自己用呢?下面小編和大家來一起學習下吧
    2019-05-05
  • vue-cli3 從搭建到優(yōu)化的詳細步驟

    vue-cli3 從搭建到優(yōu)化的詳細步驟

    這篇文章主要介紹了vue-cli3 從搭建到優(yōu)化的詳細步驟,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2019-01-01
  • Vue實現(xiàn)通知或詳情類彈窗

    Vue實現(xiàn)通知或詳情類彈窗

    這篇文章主要為大家詳細介紹了Vue實現(xiàn)通知或詳情類彈窗,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • vue項目如何引入json數(shù)據(jù)

    vue項目如何引入json數(shù)據(jù)

    這篇文章主要介紹了vue項目如何引入json數(shù)據(jù),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-10-10

最新評論

汝州市| 巴彦淖尔市| 泰宁县| 珲春市| 社旗县| 盐城市| 麻阳| 马尔康县| 潜江市| 许昌县| 综艺| 沙湾县| 舞钢市| 思南县| 敦化市| 遵义县| 罗江县| 赤水市| 余庆县| 平南县| 托里县| 桃园县| 阿拉善盟| 汝州市| 安徽省| 泰和县| 修水县| 万年县| 青田县| 肇州县| 辽宁省| 壶关县| 宜兰县| 北流市| 新平| 彭水| 巴里| 工布江达县| 峨眉山市| 长春市| 台东县|