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

Vue3+TypeScript項(xiàng)目架構(gòu)實(shí)踐指南

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

1. 引言

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

一個(gè)良好的項(xiàng)目架構(gòu)能夠:

  • 提高代碼的可維護(hù)性和可擴(kuò)展性
  • 減少團(tuán)隊(duì)協(xié)作中的沖突和問題
  • 加速開發(fā)效率和代碼質(zhì)量
  • 便于測(cè)試和部署

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

2. 項(xiàng)目結(jié)構(gòu)設(shè)計(jì)

2.1 目錄結(jié)構(gòu)組織

一個(gè)合理的目錄結(jié)構(gòu)是項(xiàng)目架構(gòu)的基礎(chǔ),它能夠清晰地分離不同功能模塊,便于開發(fā)者理解和維護(hù)代碼。

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

2.2 目錄結(jié)構(gòu)設(shè)計(jì)原則

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

2.3 模塊劃分策略

按業(yè)務(wù)功能劃分

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

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

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

按技術(shù)類型劃分

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

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

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

混合劃分策略

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

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

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

2.4 推薦的目錄結(jié)構(gòu)

根據(jù)項(xiàng)目規(guī)模和團(tuán)隊(duì)習(xí)慣,推薦使用以下目錄結(jié)構(gòu):

// 推薦的目錄結(jié)構(gòu)
// 該結(jié)構(gòu)根據(jù)項(xiàng)目規(guī)模和團(tuán)隊(duì)習(xí)慣,綜合考慮了技術(shù)管理和業(yè)務(wù)邏輯的平衡
// 每個(gè)目錄都有明確的職責(zé),便于開發(fā)者理解和維護(hù)代碼

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

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

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

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

狀態(tài)管理結(jié)構(gòu)設(shè)計(jì)

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

Store 組織方式

// stores/user.ts
// 用戶狀態(tài)管理 Store
// 該文件使用 Pinia 的 Composition API 風(fēng)格定義用戶相關(guān)的狀態(tài)管理
// 包含用戶信息、認(rèn)證狀態(tài)、登錄/登出等功能
// 特點(diǎn):類型安全,代碼結(jié)構(gòu)清晰,邏輯組織合理
// 設(shè)計(jì)意圖:通過 Pinia 管理用戶認(rèn)證狀態(tài)和用戶信息,實(shí)現(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
 * 管理用戶認(rèn)證狀態(tài)、用戶信息等
 * 使用 Composition API 風(fēng)格定義,提供更好的類型推斷和代碼組織
 * @returns 用戶狀態(tài)管理 Store 實(shí)例
 */
export const useUserStore = defineStore('user', () => {
  // 狀態(tài)(State)
  const currentUser = ref<User | null>(null) // 當(dāng)前用戶信息,類型為 User 或 null
  const token = ref<string | null>(null) // 認(rèn)證令牌,用于 API 調(diào)用的身份驗(yàn)證
  const loading = ref(false) // 加載狀態(tài),用于控制加載指示器的顯示
  const error = ref<string | null>(null) // 錯(cuò)誤信息,用于顯示錯(cuò)誤提示

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

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

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

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

  /**
   * 初始化用戶狀態(tài)
   * 從本地存儲(chǔ)恢復(fù)令牌并獲取用戶信息
   * 在應(yīng)用啟動(dòng)時(shí)調(diào)用,用于保持用戶登錄狀態(tài)
   */
  function initialize() {
    const storedToken = localStorage.getItem('token') // 從 localStorage 獲取存儲(chǔ)的令牌
    if (storedToken) {
      token.value = storedToken // 更新認(rèn)證令牌
      fetchCurrentUser() // 獲取用戶信息
    }
  }

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

3.2 跨 Store 通信策略

在復(fù)雜的應(yīng)用中,不同 Store 之間可能需要進(jìn)行通信。以下是幾種跨 Store 通信的策略:

1. 直接引用其他 Store

在一個(gè) Store 中直接引用和使用另一個(gè) Store。

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

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

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

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

  return {
    getUserCart
  }
})

2. 使用事件總線

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

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

import mitt from 'mitt'

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

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

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

/**
 * 用戶狀態(tài)管理 Store
 * @returns 用戶狀態(tài)管理 Store 實(shí)例
 */
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
// 購(gòu)物車狀態(tài)管理 Store
// 該文件演示了如何通過事件總線監(jiān)聽事件
// 設(shè)計(jì)意圖:監(jiān)聽用戶登錄事件,同步購(gòu)物車數(shù)據(jù)

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

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

  return {
    initialize
  }
})

3. 使用 Composition API 共享邏輯

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

// composables/useApi.ts
// API 調(diào)用邏輯
// 該文件演示了如何通過 Composition API 封裝共享邏輯
// 優(yōu)點(diǎn):邏輯復(fù)用性高,Store 之間耦合度低
// 設(shè)計(jì)意圖:封裝 API 請(qǐng)求的通用邏輯,供多個(gè) Store 和組件復(fù)用

import { ref } from 'vue'

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

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

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

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

import { defineStore } from 'pinia'
import { useApi } from '@/composables/useApi'
import { userApi } from '@/services/api/user' // 假設(shè)已導(dǎo)入

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

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

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

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

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

1. 使用 localStorage/sessionStorage

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

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

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

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

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

2. 使用 pinia-plugin-persistedstate

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

import { defineStore } from 'pinia'
import { ref } from 'vue'
import type { User } from '@/models/user' // 假設(shè)已導(dǎo)入

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

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

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

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

4. 路由架構(gòu)

4.1 路由配置組織

一個(gè)合理的路由配置能夠清晰地定義應(yīng)用的導(dǎo)航結(jié)構(gòu),便于開發(fā)者理解和維護(hù)。

路由配置文件結(jié)構(gòu)

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

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

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

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

export default router

// router/routes.ts
// 路由定義文件
// 該文件負(fù)責(zé)定義應(yīng)用的所有路由
// 設(shè)計(jì)意圖:集中管理應(yīng)用的路由配置,包括路由路徑、組件映射、元信息等

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

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

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

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

// router/guards.ts
// 路由守衛(wèi)文件
// 該文件負(fù)責(zé)定義路由守衛(wèi)邏輯
// 設(shè)計(jì)意圖:實(shí)現(xiàn)路由的認(rèn)證和權(quán)限控制,保護(hù)需要認(rèn)證的路由

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

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

4.2 路由模塊化

對(duì)于大型應(yīng)用,可以將路由配置按照模塊進(jìn)行拆分,提高代碼的可維護(hù)性。

// router/modules/auth.ts
// 認(rèn)證模塊路由配置
// 設(shè)計(jì)意圖:將認(rèn)證相關(guān)的路由配置拆分到獨(dú)立的模塊,提高代碼可維護(hù)性

import { AppRouteRecordRaw } from '../types'

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

// router/modules/dashboard.ts
// 儀表盤模塊路由配置
// 設(shè)計(jì)意圖:將儀表盤相關(guān)的路由配置拆分到獨(dú)立的模塊,提高代碼可維護(hù)性

import { AppRouteRecordRaw } from '../types'

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

// router/routes.ts
// 路由配置主文件
// 設(shè)計(jì)意圖:組合所有路由模塊,形成完整的應(yīng)用路由配置

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

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

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

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

全局守衛(wèi)

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

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

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

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

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

路由獨(dú)享守衛(wèi)

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

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

import { AppRouteRecordRaw } from './types'

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

組件內(nèi)守衛(wèi)

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

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

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

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

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

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

5. API 層設(shè)計(jì)

5.1 API 服務(wù)組織

一個(gè)合理的 API 層設(shè)計(jì)能夠清晰地管理與后端的通信,提高代碼的可維護(hù)性和可測(cè)試性。

API 服務(wù)文件結(jié)構(gòu)

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

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

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

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

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

export default apiClient

// services/api/user.ts
// 用戶 API 服務(wù)
// 設(shè)計(jì)意圖:封裝用戶相關(guān)的 API 調(diào)用,提供類型安全的 API 服務(wù)

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

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

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

  /**
   * 獲取當(dāng)前用戶信息
   * @returns 當(dāng)前用戶信息
   */
  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 服務(wù)
// 設(shè)計(jì)意圖:封裝產(chǎn)品相關(guān)的 API 調(diào)用,提供類型安全的 API 服務(wù)

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

/**
 * 產(chǎn)品 API 服務(wù)
 * 封裝產(chǎn)品相關(guān)的 API 調(diào)用,包括獲取產(chǎn)品列表、獲取產(chǎn)品詳情、創(chuàng)建產(chǎn)品、更新產(chǎn)品、刪除產(chǎn)品等
 */
export const productApi = {
  /**
   * 獲取產(chǎn)品列表
   * @param params 查詢參數(shù),包含分頁(yè)和篩選條件
   * @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 刪除結(jié)果
   */
  deleteProduct: (id: number) => {
    return apiClient.delete(`/products/${id}`)
  }
}

5.2 API 錯(cuò)誤處理策略

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

全局錯(cuò)誤處理

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

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

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

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

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

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

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

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

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

局部錯(cuò)誤處理

在具體的 API 調(diào)用處進(jìn)行錯(cuò)誤處理,適用于需要特殊處理的場(chǎng)景。

// composables/useAuth.ts
// 認(rèn)證相關(guān)邏輯
// 設(shè)計(jì)意圖:在具體的 API 調(diào)用處進(jìn)行錯(cuò)誤處理,適用于需要特殊處理的場(chǎng)景

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

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

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

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

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

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

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

內(nèi)存緩存

使用內(nèi)存緩存存儲(chǔ)臨時(shí)數(shù)據(jù),適用于單次會(huì)話中的數(shù)據(jù)。

// services/api/product.ts
// 產(chǎn)品 API 服務(wù)
// 設(shè)計(jì)意圖:通過內(nèi)存緩存減少重復(fù)請(qǐng)求,提高應(yīng)用性能和用戶體驗(yàn)

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

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

/**
 * 產(chǎn)品 API 服務(wù)
 */
export const productApi = {
  /**
   * 獲取產(chǎn)品列表
   * 使用內(nèi)存緩存減少重復(fù)請(qǐng)求,提高應(yīng)用性能
   * @param params 查詢參數(shù),包含分頁(yè)和篩選條件
   * @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 請(qǐng)求
    const data = await apiClient.get<Product[]>('/products', { params })
    
    // 更新緩存
    // 存儲(chǔ)響應(yīng)數(shù)據(jù)和當(dāng)前時(shí)間戳
    productsCache = {
      data,
      timestamp: Date.now()
    }

    return data
  },

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

本地存儲(chǔ)緩存

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

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

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

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

/**
 * 用戶 API 服務(wù)
 */
export const userApi = {
  /**
   * 獲取當(dāng)前用戶信息
   * 使用本地存儲(chǔ)緩存實(shí)現(xiàn)數(shù)據(jù)持久化,減少重復(fù)請(qǐng)求
   * @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 請(qǐng)求
    const data = await apiClient.get<User>('/users/me')
    
    // 更新緩存
    // 將響應(yīng)數(shù)據(jù)和當(dāng)前時(shí)間戳存儲(chǔ)到本地存儲(chǔ)
    localStorage.setItem(USER_CACHE_KEY, JSON.stringify({
      data,
      timestamp: Date.now()
    }))

    return data
  },

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

6. 工具類和公共組件

6.1 工具函數(shù)組織

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

工具函數(shù)目錄結(jié)構(gòu)

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

工具函數(shù)示例

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

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

/**
 * 格式化日期
 * 將日期對(duì)象、字符串或時(shí)間戳格式化為指定格式的日期字符串
 * @param date 日期,可以是 Date 對(duì)象、日期字符串或時(shí)間戳
 * @param format 格式化模板,默認(rèn)為 '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 對(duì)象
  const year = d.getFullYear() // 獲取年份
  const month = String(d.getMonth() + 1).padStart(2, '0') // 獲取月份(0-11,所以加 1),并補(bǔ)零
  const day = String(d.getDate()).padStart(2, '0') // 獲取日期,并補(bǔ)零
  const hours = String(d.getHours()).padStart(2, '0') // 獲取小時(shí),并補(bǔ)零
  const minutes = String(d.getMinutes()).padStart(2, '0') // 獲取分鐘,并補(bǔ)零
  const seconds = String(d.getSeconds()).padStart(2, '0') // 獲取秒數(shù),并補(bǔ)零

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

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

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

/**
 * 驗(yàn)證密碼強(qiáng)度
 * 根據(jù)密碼長(zhǎng)度、包含的字符類型等計(jì)算密碼強(qiáng)度等級(jí)
 * @param password 密碼,字符串類型
 * @returns 密碼強(qiáng)度等級(jí) (0-4),數(shù)字越大表示密碼強(qiáng)度越高
 */
export function getPasswordStrength(password: string): number {
  let strength = 0 // 初始強(qiáng)度為 0
  if (password.length >= 8) strength++ // 長(zhǎng)度至少 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 // 返回密碼強(qiáng)度等級(jí)
}

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

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

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

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

6.2 公共組件組織

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

公共組件目錄結(jié)構(gòu)

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

公共組件設(shè)計(jì)原則

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

公共組件示例

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

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

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

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

/**
 * 按鈕尺寸
 * 支持多種預(yù)設(shè)的按鈕尺寸
 */
export type ButtonSize = 'sm' | 'md' | 'lg'

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

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

<style scoped>
/* 按鈕基礎(chǔ)樣式 */
.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;
}

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

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

6.3 組合式 API 邏輯組織

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

組合式 API 目錄結(jié)構(gòu)

src/composables/
├── useAuth.ts         # 認(rèn)證相關(guān)邏輯
├── useApi.ts          # API 調(diào)用邏輯
├── useLocalStorage.ts # 本地存儲(chǔ)邏輯
├── useValidation.ts   # 驗(yàn)證相關(guān)邏輯
├── useDebounce.ts     # 防抖邏輯
└── useThrottle.ts     # 節(jié)流邏輯

組合式 API 示例

// composables/useAuth.ts
// 認(rèn)證相關(guān)邏輯
// 設(shè)計(jì)意圖:封裝認(rèn)證相關(guān)的邏輯,供多個(gè)組件復(fù)用,實(shí)現(xiàn)邏輯的模塊化和可維護(hù)性

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

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

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

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

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

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

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

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

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

// composables/useApi.ts
// API 調(diào)用邏輯
// 設(shè)計(jì)意圖:封裝 API 請(qǐng)求的通用邏輯,供多個(gè)組件和 composables 復(fù)用,實(shí)現(xiàn)錯(cuò)誤處理和加載狀態(tài)的統(tǒng)一管理

import { ref } from 'vue'

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

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

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

7. 企業(yè)級(jí)項(xiàng)目案例分析

7.1 項(xiàng)目架構(gòu)演進(jìn)

初始架構(gòu)

在項(xiàng)目初期,通常會(huì)采用相對(duì)簡(jiǎn)單的架構(gòu),以快速實(shí)現(xiàn)核心功能。

// 初始架構(gòu)
// 項(xiàng)目初期的簡(jiǎn)單架構(gòu),以快速實(shí)現(xiàn)核心功能為目標(biāo)
// 設(shè)計(jì)意圖:在項(xiàng)目初期,使用簡(jiǎn)單的目錄結(jié)構(gòu),快速搭建項(xiàng)目框架

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

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

演進(jìn)架構(gòu)

隨著項(xiàng)目規(guī)模的擴(kuò)大,架構(gòu)會(huì)逐漸演進(jìn),添加更多的功能模塊和組織結(jié)構(gòu)。

// 演進(jìn)架構(gòu)
// 隨著項(xiàng)目規(guī)模擴(kuò)大,架構(gòu)逐漸演進(jìn),添加更多功能模塊和組織結(jié)構(gòu)
// 設(shè)計(jì)意圖:通過完善的目錄結(jié)構(gòu),提高代碼的可維護(hù)性和可擴(kuò)展性,適應(yīng)項(xiàng)目的不斷發(fā)展

// 特點(diǎn):
// - 目錄結(jié)構(gòu)更加完善,功能模塊劃分更加細(xì)致
// - 引入了更多的 TypeScript 相關(guān)目錄,如 types、enums 等
// - 使用 Pinia 替代 Vuex,采用 Composition API 風(fēng)格
// - 增加了更多的功能模塊,如 composables、layouts、models 等

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

微前端架構(gòu)

對(duì)于超大型應(yīng)用,可以考慮采用微前端架構(gòu),將應(yīng)用拆分為多個(gè)獨(dú)立的微應(yīng)用。

// 微前端架構(gòu)
// 對(duì)于超大型應(yīng)用,采用微前端架構(gòu),將應(yīng)用拆分為多個(gè)獨(dú)立的微應(yīng)用
// 設(shè)計(jì)意圖:通過微前端架構(gòu),實(shí)現(xiàn)應(yīng)用的模塊化和獨(dú)立部署,提高開發(fā)效率和團(tuán)隊(duì)協(xié)作

// 特點(diǎn):
// - 將應(yīng)用拆分為多個(gè)獨(dú)立的微應(yīng)用,每個(gè)微應(yīng)用可以獨(dú)立開發(fā)、部署和維護(hù)
// - 主應(yīng)用(shell)負(fù)責(zé)微應(yīng)用的加載和管理
// - 共享資源(packages)供多個(gè)微應(yīng)用復(fù)用
// - 提高了應(yīng)用的可擴(kuò)展性和可維護(hù)性

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

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

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

  1. 性能優(yōu)化

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

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

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

    • 認(rèn)證和授權(quán)
    • 數(shù)據(jù)驗(yàn)證和 sanitization
    • 防止 XSS 和 CSRF 攻擊

7.3 實(shí)際項(xiàng)目案例

電商平臺(tái)

架構(gòu)特點(diǎn)

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

核心模塊

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

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

架構(gòu)特點(diǎn)

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

核心模塊

  • 用戶管理模塊:處理用戶信息、角色、權(quán)限等
  • 組織管理模塊:處理部門、職位、員工等
  • 資產(chǎn)管理模塊:處理資產(chǎn)登記、折舊、盤點(diǎn)等
  • 財(cái)務(wù)管理模塊:處理預(yù)算、報(bào)銷、審批等
  • 項(xiàng)目管理模塊:處理項(xiàng)目計(jì)劃、任務(wù)、進(jìn)度等

8. 總結(jié)與實(shí)踐

8.1 項(xiàng)目架構(gòu)實(shí)踐

  1. 合理的目錄結(jié)構(gòu)

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

    • 使用 Pinia 進(jìn)行狀態(tài)管理
    • 按模塊劃分 Store
    • 合理使用持久化存儲(chǔ)
  3. 路由架構(gòu)設(shè)計(jì)

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

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

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

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

8.2 團(tuán)隊(duì)協(xié)作實(shí)踐

  1. 代碼規(guī)范

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

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

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

    • CI/CD 流程
    • 自動(dòng)化測(cè)試
    • 代碼質(zhì)量檢測(cè)

8.3 未來發(fā)展趨勢(shì)

  1. 微前端架構(gòu)

    • 將大型應(yīng)用拆分為多個(gè)獨(dú)立的微應(yīng)用
    • 提高團(tuán)隊(duì)協(xié)作效率和應(yīng)用可維護(hù)性
  2. Serverless 架構(gòu)

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

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

    • 部分計(jì)算密集型任務(wù)使用 WebAssembly
    • 提高應(yīng)用性能
  5. 邊緣計(jì)算

    • 將部分計(jì)算和存儲(chǔ)遷移到邊緣節(jié)點(diǎn)
    • 減少延遲和提高用戶體驗(yàn)

9. 附錄

9.1 技術(shù)棧推薦

| 類別 | 技術(shù) | 版本 | 用途 |

總結(jié)

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

到此這篇關(guān)于Vue3+TypeScript項(xiàng)目架構(gòu)實(shí)踐指南的文章就介紹到這了,更多相關(guān)Vue3+TS項(xiàng)目架構(gòu)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 一文教會(huì)你搭建vite項(xiàng)目并配置路由和element-plus

    一文教會(huì)你搭建vite項(xiàng)目并配置路由和element-plus

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

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

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

    vue2項(xiàng)目使用exceljs多表頭導(dǎo)出功能詳解

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

最新評(píng)論

溧阳市| 永德县| 日照市| 郓城县| 南部县| 正阳县| 房山区| 杭锦旗| 邢台市| 台东县| 特克斯县| 肇东市| 林西县| 紫阳县| 昌黎县| 高雄市| 贺州市| 玉门市| 安康市| 客服| 蒲城县| 铜鼓县| 阜平县| 嵩明县| 临湘市| 馆陶县| 贵港市| 新民市| 仙游县| 黔东| 鸡泽县| 宝清县| 乐业县| 儋州市| 陆河县| 阿拉善左旗| 璧山县| 克什克腾旗| 东方市| 潮安县| 陆良县|