Vue 3 + TypeScript 项目架构实践

Vue 3 + TypeScript 项目架构实践

1. 引言

Vue 3 + TypeScript + Vite + Pinia 技术栈已经成为现代前端开发的主流选择,其优秀的性能和开发体验备受开发者青睐。然而,要充分发挥这一技术栈的潜力,合理的项目架构设计至关重要。

一个良好的项目架构能够:

  • 提高代码的可维护性和可扩展性
  • 减少团队协作中的冲突和问题
  • 加速开发效率和代码质量
  • 便于测试和部署

本文将详细介绍 Vue 3 + TypeScript 项目的架构实践,包括项目结构设计、状态管理架构、路由架构、API 层设计、工具类和公共组件的组织,以及企业级项目案例分析。

2. 项目结构设计

2.1 目录结构组织

一个合理的目录结构是项目架构的基础,它能够清晰地分离不同功能模块,便于开发者理解和维护代码。

复制代码
// 推荐的 Vue 3 + TypeScript 项目目录结构
// 该结构清晰分离了不同功能模块,便于开发者理解和维护代码
// 每个目录都有明确的职责和用途,遵循关注点分离原则
// 设计意图:通过模块化组织提高代码可维护性和可扩展性
src/                             // 源代码目录
├── assets/            # 静态资源文件
│   ├── images/        # 图片资源(如产品图片、背景图等)
│   ├── styles/        # 全局样式(如重置样式、主题样式等)
│   └── icons/         # 图标资源(如 SVG 图标、字体图标等)
├── components/        # 公共组件
│   ├── common/        # 通用基础组件(如按钮、输入框等)
│   ├── layout/        # 布局相关组件(如头部、侧边栏等)
│   └── business/      # 业务相关组件(如用户卡片、产品列表等)
├── composables/       # 组合式 API 逻辑
│   ├── useAuth.ts     # 认证相关逻辑(如登录、注册等)
│   ├── useApi.ts      # API 调用逻辑(如请求封装、错误处理等)
│   └── useLocalStorage.ts # 本地存储逻辑(如数据持久化等)
├── constants/         # 常量定义
│   ├── api.ts         # API 相关常量(如接口地址、请求超时等)
│   ├── routes.ts      # 路由相关常量(如路由名称、路径等)
│   └── storage.ts     # 存储相关常量(如存储键名、过期时间等)
├── enums/             # 枚举类型定义
│   ├── user.ts        # 用户相关枚举(如用户角色、状态等)
│   └── status.ts      # 状态相关枚举(如订单状态、审批状态等)
├── hooks/             # 自定义钩子(与 composables 类似,可根据团队习惯选择其一)
├── layouts/           # 布局组件
│   ├── DefaultLayout.vue # 默认布局(如包含侧边栏和头部的布局)
│   └── AuthLayout.vue    # 认证布局(如登录、注册页面的布局)
├── models/            # 数据模型定义
│   ├── user.ts        # 用户模型(如用户信息结构、类型定义等)
│   ├── product.ts     # 产品模型(如产品信息结构、类型定义等)
│   └── common.ts      # 通用模型(如分页结构、响应结构等)
├── router/            # 路由配置
│   ├── index.ts       # 路由主配置(如创建路由实例、应用守卫等)
│   ├── routes.ts      # 路由定义(如路由路径、组件映射等)
│   └── guards.ts      # 路由守卫(如认证守卫、权限守卫等)
├── services/          # 服务层
│   ├── api/           # API 服务
│   │   ├── user.ts    # 用户相关 API(如登录、获取用户信息等)
│   │   ├── product.ts # 产品相关 API(如获取产品列表、创建产品等)
│   │   └── index.ts   # API 服务主文件(如创建 axios 实例、配置拦截器等)
│   └── utils/         # 工具服务(如第三方服务集成、业务工具等)
├── stores/            # Pinia 状态管理
│   ├── user.ts        # 用户状态(如用户信息、认证状态等)
│   ├── product.ts     # 产品状态(如产品列表、详情等)
│   └── common.ts      # 通用状态(如全局加载状态、错误信息等)
├── types/             # TypeScript 类型定义
│   ├── api.ts         # API 相关类型(如请求参数、响应类型等)
│   ├── components.ts  # 组件相关类型(如 props 类型、事件类型等)
│   └── common.ts      # 通用类型(如通用接口、工具类型等)
├── utils/             # 工具函数
│   ├── format.ts      # 格式化工具(如日期格式化、金额格式化等)
│   ├── validation.ts  # 验证工具(如邮箱验证、密码强度验证等)
│   └── storage.ts     # 存储工具(如本地存储封装、会话存储封装等)
├── views/             # 页面组件
│   ├── auth/          # 认证相关页面
│   │   ├── Login.vue  # 登录页面
│   │   └── Register.vue # 注册页面
│   ├── dashboard/     # 仪表盘页面
│   └── products/      # 产品相关页面
├── App.vue            # 根组件(应用的入口组件)
├── main.ts            # 应用入口(如初始化 Vue 应用、注册插件等)
└── env.d.ts           # 环境变量类型定义(如 Vite 环境变量类型声明)

2.2 目录结构设计原则

  1. 功能模块化:将相关功能的代码组织在一起,便于理解和维护
  2. 关注点分离:将不同职责的代码分离到不同目录,如视图、组件、状态管理等
  3. 层次清晰:建立清晰的代码层次结构,如 API 层、服务层、业务逻辑层等
  4. 可扩展性:预留合理的扩展空间,便于后续功能的添加和修改
  5. 命名规范:采用一致的命名规范,提高代码的可读性

2.3 模块划分策略

按业务功能划分

将代码按照业务功能进行划分,每个功能模块包含完整的组件、状态、服务等。

复制代码
// 按业务功能划分的目录结构
// 该结构将代码按照业务功能进行划分,每个功能模块包含完整的组件、状态、服务等
// 优点:业务逻辑内聚,便于团队协作和代码维护
// 缺点:可能导致某些通用代码重复

// 适用场景:大型应用,多个团队负责不同业务模块
// 设计意图:通过业务模块划分,实现团队协作的隔离和业务逻辑的内聚
src/
├── modules/            # 业务模块目录(存放各个独立的业务模块)
│   ├── auth/          # 认证模块(处理登录、注册等认证相关功能)
│   │   ├── components/ # 认证相关组件(如登录表单、注册表单等)
│   │   ├── views/      # 认证相关页面(如登录页面、注册页面等)
│   │   ├── services/   # 认证相关服务(如认证 API 调用等)
│   │   └── stores/     # 认证相关状态(如认证状态管理等)
│   ├── product/        # 产品模块(处理产品相关功能)
│   │   ├── components/ # 产品相关组件(如产品卡片、产品列表项等)
│   │   ├── views/      # 产品相关页面(如产品列表页、产品详情页等)
│   │   ├── services/   # 产品相关服务(如产品 API 调用等)
│   │   └── stores/     # 产品相关状态(如产品列表状态、详情状态等)
│   └── user/           # 用户模块(处理用户相关功能)
│       ├── components/ # 用户相关组件(如用户信息卡片、用户列表项等)
│       ├── views/      # 用户相关页面(如用户列表页、用户详情页等)
│       ├── services/   # 用户相关服务(如用户 API 调用等)
│       └── stores/     # 用户相关状态(如用户列表状态、详情状态等)
└── shared/             # 共享资源(存放各个模块共用的代码)
    ├── components/     # 共享组件(如通用按钮、输入框等)
    ├── composables/    # 共享组合式 API(如通用认证逻辑、API 调用逻辑等)
    ├── utils/          # 共享工具函数(如格式化工具、验证工具等)
    └── types/          # 共享类型定义(如通用接口、类型声明等)
按技术类型划分

将代码按照技术类型进行划分,如组件、服务、状态管理等。

复制代码
// 按技术类型划分的目录结构
// 该结构将代码按照技术类型进行划分,如组件、服务、状态管理等
// 优点:技术职责清晰,便于代码复用和维护
// 缺点:业务逻辑分散在不同目录,可能增加跨模块理解难度

// 适用场景:中小型应用,团队成员技术栈全面
// 设计意图:通过技术类型划分,实现代码的分类管理和复用
src/
├── components/         # 所有组件(按技术类型组织的组件集合)
├── services/           # 所有服务(按技术类型组织的服务集合)
├── stores/             # 所有状态管理(按技术类型组织的状态管理集合)
├── views/              # 所有页面(按技术类型组织的页面集合)
└── utils/              # 所有工具函数(按技术类型组织的工具函数集合)
混合划分策略

结合业务功能和技术类型的划分策略,在顶层按技术类型划分,在具体模块内按业务功能划分。

复制代码
// 混合划分策略的目录结构
// 该结构结合业务功能和技术类型的划分策略,在顶层按技术类型划分,在具体模块内按业务功能划分
// 优点:既保持了技术职责清晰,又保证了业务逻辑的内聚性
// 缺点:目录结构可能相对复杂

// 适用场景:中大型应用,需要平衡技术管理和业务逻辑
// 设计意图:通过混合划分策略,兼顾技术管理的清晰性和业务逻辑的内聚性
src/
├── components/         # 所有组件(顶层按技术类型划分)
│   ├── common/         # 通用组件(如按钮、输入框等通用基础组件)
│   ├── auth/           # 认证相关组件(如登录表单、注册表单等)
│   └── product/        # 产品相关组件(如产品卡片、产品列表等)
├── services/           # 所有服务(顶层按技术类型划分)
│   ├── api/            # API 服务(按业务功能划分的 API 服务)
│   │   ├── auth.ts     # 认证 API(处理认证相关的 API 调用)
│   │   └── product.ts  # 产品 API(处理产品相关的 API 调用)
│   └── utils/          # 服务工具(如 API 请求封装、错误处理等)
└── stores/             # 所有状态管理(顶层按技术类型划分)
    ├── auth.ts         # 认证状态(处理认证相关的状态管理)
    └── product.ts      # 产品状态(处理产品相关的状态管理)

2.4 推荐的目录结构

根据项目规模和团队习惯,推荐使用以下目录结构:

复制代码
// 推荐的目录结构
// 该结构根据项目规模和团队习惯,综合考虑了技术管理和业务逻辑的平衡
// 每个目录都有明确的职责,便于开发者理解和维护代码

// 适用场景:大多数 Vue 3 + TypeScript 项目
// 设计意图:提供一个通用的、可扩展的目录结构,适用于各种规模的项目
src/
├── assets/            # 静态资源(如图片、样式、图标等)
├── components/        # 公共组件(如通用组件、业务组件等)
├── composables/       # 组合式 API(如认证逻辑、API 调用逻辑等)
├── constants/         # 常量定义(如 API 地址、路由名称等)
├── enums/             # 枚举类型(如用户角色、订单状态等)
├── layouts/           # 布局组件(如默认布局、认证布局等)
├── models/            # 数据模型(如用户模型、产品模型等)
├── router/            # 路由配置(如路由定义、守卫等)
├── services/          # 服务层(如 API 服务、工具服务等)
├── stores/            # Pinia 状态管理(如用户状态、产品状态等)
├── types/             # TypeScript 类型(如 API 类型、组件类型等)
├── utils/             # 工具函数(如格式化工具、验证工具等)
├── views/             # 页面组件(如认证页面、仪表盘页面等)
├── App.vue            # 根组件(应用的入口组件)
├── main.ts            # 入口文件(初始化 Vue 应用、注册插件等)
└── env.d.ts           # 环境变量类型(Vite 环境变量类型声明)

3. 状态管理架构

3.1 Pinia 状态管理实践

Pinia 作为 Vue 3 官方推荐的状态管理库,提供了更简洁、更灵活的状态管理方案。

状态管理结构设计
  1. 按模块划分 Store:将状态按照业务模块进行划分,每个模块对应一个 Store
  2. 使用 Composition API:采用 Composition API 风格定义 Store,提高代码的可读性和可维护性
  3. 类型安全:充分利用 TypeScript 的类型系统,为 Store 定义明确的类型
Store 组织方式
typescript 复制代码
// stores/user.ts
// 用户状态管理 Store
// 该文件使用 Pinia 的 Composition API 风格定义用户相关的状态管理
// 包含用户信息、认证状态、登录/登出等功能
// 特点:类型安全,代码结构清晰,逻辑组织合理
// 设计意图:通过 Pinia 管理用户认证状态和用户信息,实现状态的集中管理和持久化

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

/**
 * 用户状态管理 Store
 * 管理用户认证状态、用户信息等
 * 使用 Composition API 风格定义,提供更好的类型推断和代码组织
 * @returns 用户状态管理 Store 实例
 */
export const useUserStore = defineStore('user', () => {
  // 状态(State)
  const currentUser = ref<User | null>(null) // 当前用户信息,类型为 User 或 null
  const token = ref<string | null>(null) // 认证令牌,用于 API 调用的身份验证
  const loading = ref(false) // 加载状态,用于控制加载指示器的显示
  const error = ref<string | null>(null) // 错误信息,用于显示错误提示

  // Getters(计算属性)
  const isAuthenticated = computed(() => !!token.value) // 是否已认证,根据 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 // 结束加载
    }
  }

  /**
   * 初始化用户状态
   * 从本地存储恢复令牌并获取用户信息
   * 在应用启动时调用,用于保持用户登录状态
   */
  function initialize() {
    const storedToken = localStorage.getItem('token') // 从 localStorage 获取存储的令牌
    if (storedToken) {
      token.value = storedToken // 更新认证令牌
      fetchCurrentUser() // 获取用户信息
    }
  }

  return {
    // 状态(State)
    currentUser,
    token,
    loading,
    error,
    // Getters(计算属性)
    isAuthenticated,
    userDisplayName,
    // Actions(方法)
    login,
    logout,
    fetchCurrentUser,
    initialize
  }
})

3.2 跨 Store 通信策略

在复杂的应用中,不同 Store 之间可能需要进行通信。以下是几种跨 Store 通信的策略:

1. 直接引用其他 Store

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

typescript 复制代码
// stores/cart.ts
// 购物车状态管理 Store
// 该文件演示了如何通过直接引用其他 Store 来实现跨 Store 通信
// 优点:代码简洁直接,易于理解
// 缺点:Store 之间耦合度较高
// 设计意图:演示跨 Store 通信的直接引用方法,适用于简单场景

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

/**
 * 购物车状态管理 Store
 * 管理购物车商品、数量等
 * @returns 购物车状态管理 Store 实例
 */
export const useCartStore = defineStore('cart', () => {
  // 直接引用用户 Store
  // 优点:代码简洁直接,易于理解
  // 缺点:Store 之间耦合度较高,可能导致循环依赖
  const userStore = useUserStore()

  /**
   * 获取用户购物车
   * 根据用户认证状态获取对应的购物车数据
   * 演示了如何使用其他 Store 的状态(isAuthenticated)
   */
  function getUserCart() {
    if (userStore.isAuthenticated) {
      // 获取登录用户的购物车
      // 当用户已认证时,获取与用户账号关联的购物车
      console.log('Getting cart for authenticated user')
    } else {
      // 获取访客购物车
      // 当用户未认证时,获取基于本地存储的访客购物车
      console.log('Getting cart for guest user')
    }
  }

  return {
    getUserCart
  }
})
2. 使用事件总线

通过事件总线在不同 Store 之间传递消息。

typescript 复制代码
// utils/eventBus.ts
// 事件总线工具
// 用于在不同组件和 Store 之间传递消息
// 基于 mitt 库实现
// 设计意图:通过事件总线实现组件和 Store 之间的解耦通信

import mitt from 'mitt'

/**
 * 事件总线实例
 * 用于跨组件和跨 Store 通信
 * 优点:组件和 Store 之间解耦,减少直接依赖
 * 缺点:事件流难以追踪,可能导致调试困难
 */
export const eventBus = mitt()

// stores/user.ts
// 用户状态管理 Store
// 该文件演示了如何通过事件总线发送事件
// 设计意图:登录成功后通知其他模块,实现跨模块通信

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

/**
 * 用户状态管理 Store
 * @returns 用户状态管理 Store 实例
 */
export const useUserStore = defineStore('user', () => {
  /**
   * 用户登录
   * 登录成功后通过事件总线通知其他 Store
   * 演示了如何通过事件总线发送事件
   */
  function login() {
    // 登录逻辑
    console.log('User logging in...')
    // 登录成功后发送事件
    // 事件名称:'user:login',事件数据:{ userId: 123 }
    eventBus.emit('user:login', { userId: 123 })
  }

  return {
    login
  }
})

// stores/cart.ts
// 购物车状态管理 Store
// 该文件演示了如何通过事件总线监听事件
// 设计意图:监听用户登录事件,同步购物车数据

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

/**
 * 购物车状态管理 Store
 * @returns 购物车状态管理 Store 实例
 */
export const useCartStore = defineStore('cart', () => {
  /**
   * 初始化购物车
   * 监听用户登录事件,以便同步购物车数据
   * 演示了如何通过事件总线监听事件
   */
  function initialize() {
    // 监听用户登录事件
    // 事件名称:'user:login',回调函数处理事件数据
    eventBus.on('user:login', (data) => {
      // 处理用户登录事件
      console.log('User logged in:', data.userId)
      // 可以在这里同步购物车数据
      // 例如:将访客购物车数据同步到登录用户的购物车
    })
  }

  return {
    initialize
  }
})
3. 使用 Composition API 共享逻辑

将共享逻辑提取到 composables 中,供多个 Store 使用。

typescript 复制代码
// composables/useApi.ts
// API 调用逻辑
// 该文件演示了如何通过 Composition API 封装共享逻辑
// 优点:逻辑复用性高,Store 之间耦合度低
// 设计意图:封装 API 请求的通用逻辑,供多个 Store 和组件复用

import { ref } from 'vue'

/**
 * API 调用逻辑
 * 封装了 API 请求的通用逻辑,如加载状态、错误处理等
 * 使用 Composition API 风格,提供更好的代码组织和复用性
 * @returns API 调用相关的状态和方法
 */
export function useApi() {
  const loading = ref(false) // 加载状态,用于控制加载指示器的显示
  const error = ref<string | null>(null) // 错误信息,用于显示错误提示

  /**
   * 发起 API 请求
   * 封装了 API 请求的通用逻辑,包括加载状态管理和错误处理
   * @param apiCall API 调用函数,返回 Promise
   * @returns API 响应数据
   * @throws API 请求失败时抛出错误
   */
  async function request<T>(apiCall: () => Promise<T>): Promise<T> {
    loading.value = true // 开始加载
    error.value = null // 清空错误信息
    try {
      return await apiCall() // 执行 API 调用
    } catch (err) {
      error.value = 'API request failed' // 设置错误信息
      throw err // 重新抛出错误,以便调用方处理
    } finally {
      loading.value = false // 结束加载
    }
  }

  return {
    loading, // 加载状态
    error, // 错误信息
    request // API 请求方法
  }
}

// stores/user.ts
// 用户状态管理 Store
// 该文件演示了如何通过 Composition API 共享逻辑
// 设计意图:通过使用共享的 useApi composable,减少代码重复,提高可维护性

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

/**
 * 用户状态管理 Store
 * @returns 用户状态管理 Store 实例
 */
export const useUserStore = defineStore('user', () => {
  // 使用共享的 API 逻辑
  // 优点:代码复用,减少重复逻辑,Store 之间耦合度低
  const { loading, error, request } = useApi()

  /**
   * 用户登录
   * 使用共享的 request 方法发起登录请求
   * 演示了如何使用共享的 composable 逻辑
   * @param email 邮箱
   * @param password 密码
   * @returns 登录响应,包含用户信息和令牌
   * @throws 登录失败时抛出错误
   */
  async function login(email: string, password: string) {
    return request(() => userApi.login({ email, password }))
  }

  return {
    loading, // 加载状态
    error, // 错误信息
    login // 登录方法
  }
})

3.3 状态持久化策略

对于需要持久化的状态,如用户认证信息、用户偏好设置等,可以使用以下策略:

1. 使用 localStorage/sessionStorage
typescript 复制代码
// stores/user.ts
// 用户状态管理 Store
// 该文件演示了如何通过 localStorage 实现状态持久化
// 优点:实现简单直接,适用于基本的状态持久化需求
// 缺点:需要手动管理存储逻辑,代码冗余
// 设计意图:通过 localStorage 实现状态持久化,保持用户登录状态

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

/**
 * 用户状态管理 Store
 * @returns 用户状态管理 Store 实例
 */
export const useUserStore = defineStore('user', () => {
  // 从 localStorage 初始化令牌
  // 在 Store 初始化时,从 localStorage 中读取之前存储的令牌
  const token = ref<string | null>(localStorage.getItem('token'))

  /**
   * 设置令牌
   * 同时更新 localStorage 中的令牌,实现状态持久化
   * @param newToken 新令牌,字符串或 null
   */
  function setToken(newToken: string | null) {
    token.value = newToken // 更新内存中的令牌状态
    if (newToken) {
      localStorage.setItem('token', newToken) // 存储令牌到 localStorage
    } else {
      localStorage.removeItem('token') // 清除 localStorage 中的令牌
    }
  }

  return {
    token, // 令牌状态
    setToken // 设置令牌的方法
  }
})
2. 使用 pinia-plugin-persistedstate
typescript 复制代码
// stores/user.ts
// 用户状态管理 Store
// 该文件演示了如何通过 pinia-plugin-persistedstate 实现状态持久化
// 优点:配置简单,自动管理存储逻辑,代码简洁
// 缺点:需要安装额外的插件
// 设计意图:通过 pinia-plugin-persistedstate 插件实现状态持久化,简化存储逻辑

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

/**
 * 用户状态管理 Store
 * @returns 用户状态管理 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,状态会自动持久化到 localStorage
  }

  /**
   * 用户登出
   */
  function logout() {
    currentUser.value = null // 清空当前用户信息
    token.value = null // 清空认证令牌
    // 由于配置了 persist,状态会自动从 localStorage 中移除
  }

  return {
    currentUser, // 当前用户信息
    token, // 认证令牌
    login, // 登录方法
    logout // 登出方法
  }
}, {
  persist: {
    key: 'user-store', // 存储键名,用于在 localStorage 中标识存储的数据
    storage: localStorage, // 存储方式,使用 localStorage 实现持久化
    paths: ['currentUser', 'token'] // 需要持久化的状态路径,只持久化 currentUser 和 token
  }
})

4. 路由架构

4.1 路由配置组织

一个合理的路由配置能够清晰地定义应用的导航结构,便于开发者理解和维护。

路由配置文件结构
typescript 复制代码
// router/index.ts
// 路由主配置文件
// 该文件负责创建路由实例并应用全局守卫
// 设计意图:配置和初始化 Vue Router,应用全局守卫

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

/**
 * 创建路由实例
 * 配置路由的历史模式和路由表
 * @returns 路由实例
 */
const router = createRouter({
  history: createWebHistory(), // 使用 HTML5 History API,移除 URL 中的 # 符号
  routes // 路由配置,从 ./routes 文件导入
})

/**
 * 全局前置守卫
 * 应用认证守卫,处理所有路由的认证和权限检查
 */
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[]
}

/**
 * 路由配置数组
 * 定义应用的所有路由,包括布局、页面组件、元信息等
 */
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'), // 懒加载产品管理页面
        meta: {
          title: '产品管理',
          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
// 路由守卫文件
// 该文件负责定义路由守卫逻辑
// 设计意图:实现路由的认证和权限控制,保护需要认证的路由

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

/**
 * 认证守卫
 * 处理路由的认证和权限检查
 * 在每次路由导航前执行,确保用户有足够的权限访问目标路由
 * @param to 目标路由对象
 * @param from 来源路由对象
 * @param next 导航函数,决定导航是否继续
 */
export function authGuard(to: RouteLocationNormalized, from: RouteLocationNormalized, next: NavigationGuardNext) {
  const userStore = useUserStore() // 获取用户状态管理 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 {
    // 已登录或不需要认证,继续导航
    next()
  }
}

4.2 路由模块化

对于大型应用,可以将路由配置按照模块进行拆分,提高代码的可维护性。

typescript 复制代码
// 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' // 产品模块路由
import { userRoutes } from './modules/user' // 用户模块路由

/**
 * 路由配置数组
 * 组合所有路由模块,形成完整的应用路由配置
 */
export const routes: AppRouteRecordRaw[] = [
  ...authRoutes, // 认证模块路由
  ...dashboardRoutes, // 仪表盘模块路由
  ...productRoutes, // 产品模块路由
  ...userRoutes, // 用户模块路由
  {
    path: '/:pathMatch(.*)*',
    name: 'NotFound',
    component: () => import('@/views/error/404.vue'), // 懒加载 404 页面
    meta: {
      title: '页面不存在'
    }
  }
]

4.3 路由守卫策略

路由守卫是控制路由访问权限的重要手段,合理使用路由守卫能够提高应用的安全性和用户体验。

全局守卫

全局守卫适用于所有路由,可以用于处理认证、权限检查、页面标题设置等通用逻辑。

typescript 复制代码
// router/guards.ts
// 路由守卫文件
// 设计意图:实现路由的认证和权限控制,保护需要认证的路由

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

/**
 * 认证守卫
 * 全局前置守卫,处理所有路由的认证和权限检查
 * @param to 目标路由对象
 * @param from 来源路由对象
 * @param next 导航函数,决定导航是否继续
 */
export function authGuard(to: RouteLocationNormalized, from: RouteLocationNormalized, next: NavigationGuardNext) {
  const userStore = useUserStore() // 获取用户状态管理 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 {
    // 已登录或不需要认证,继续导航
    next()
  }
}

/**
 * 全局后置守卫
 * 在路由导航完成后执行,用于处理页面加载完成后的逻辑
 * @param to 目标路由对象
 * @param from 来源路由对象
 */
export function globalAfterGuard(to: RouteLocationNormalized, from: RouteLocationNormalized) {
  // 页面加载完成后的逻辑,如埋点统计、页面性能监控等
  console.log(`Navigated from ${from.path} to ${to.path}`)
}
路由独享守卫

路由独享守卫只适用于特定路由,可以用于处理该路由的特殊逻辑。

typescript 复制代码
// router/routes.ts
// 路由定义文件
// 设计意图:演示如何使用路由独享守卫处理特定路由的特殊逻辑

import { AppRouteRecordRaw } from './types'

/**
 * 路由配置数组
 */
export const routes: AppRouteRecordRaw[] = [
  {
    path: '/admin',
    name: 'Admin',
    component: () => import('@/views/admin/index.vue'), // 懒加载管理员页面
    meta: {
      requiresAuth: true, // 需要认证
      roles: ['admin'] // 只有 admin 角色可以访问
    },
    beforeEnter: (to, from, next) => {
      // 路由独享守卫逻辑
      // 只适用于当前路由,用于处理该路由的特殊逻辑
      console.log('Entering admin route')
      // 可以在这里添加额外的权限检查或其他逻辑
      next() // 继续导航
    }
  }
]
组件内守卫

组件内守卫适用于组件级别,可以用于处理组件的进入、离开等逻辑。

vue 复制代码
<script setup lang="ts">
// 组件内守卫示例
// 设计意图:演示如何在组件级别使用路由守卫,处理组件的进入、离开等逻辑

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

/**
 * 路由进入组件前
 * 在路由进入组件之前执行,此时组件实例还未创建
 * @param to 目标路由对象
 * @param from 来源路由对象
 * @param next 导航函数,决定导航是否继续
 */
onBeforeRouteEnter((to, from, next) => {
  console.log('Before route enter')
  // 可以在这里进行数据预加载、权限检查等
  // 例如:获取组件需要的数据,避免组件渲染时出现空白
  next() // 继续导航
})

/**
 * 路由离开组件前
 * 在路由离开组件之前执行,此时组件实例仍然存在
 * @param to 目标路由对象
 * @param from 来源路由对象
 * @param next 导航函数,决定导航是否继续
 */
onBeforeRouteLeave((to, from, next) => {
  console.log('Before route leave')
  // 可以在这里进行表单验证、确认提示等
  // 例如:检查用户是否有未保存的修改,提示用户确认离开
  next() // 继续导航
})

/**
 * 路由更新但组件被复用时
 * 在路由更新但组件被复用时执行,例如从 /user/1 导航到 /user/2
 * @param to 目标路由对象
 * @param from 来源路由对象
 * @param next 导航函数,决定导航是否继续
 */
onBeforeRouteUpdate((to, from, next) => {
  console.log('Before route update')
  // 可以在这里更新组件数据、重新获取数据等
  // 例如:根据新的路由参数重新获取用户信息
  next() // 继续导航
})
</script>

5. API 层设计

5.1 API 服务组织

一个合理的 API 层设计能够清晰地管理与后端的通信,提高代码的可维护性和可测试性。

API 服务文件结构
typescript 复制代码
// services/api/index.ts
// API 客户端配置文件
// 设计意图:创建和配置 axios 实例,设置请求/响应拦截器,统一处理 API 调用

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

/**
 * 创建 axios 实例
 * 配置基础 URL、超时时间、默认请求头等
 */
const apiClient = axios.create({
  baseURL: import.meta.env.VITE_API_BASE_URL || '/api', // API 基础 URL,从环境变量获取或使用默认值
  timeout: 10000, // 请求超时时间,10秒
  headers: {
    'Content-Type': 'application/json' // 默认请求头,使用 JSON 格式
  }
})

/**
 * 请求拦截器
 * 在发送请求之前执行,用于添加认证令牌、请求日志等
 */
apiClient.interceptors.request.use(
  (config) => {
    const userStore = useUserStore() // 获取用户状态管理 Store
    const token = userStore.token // 获取认证令牌
    
    if (token) {
      // 如果有令牌,添加到请求头的 Authorization 字段
      config.headers.Authorization = `Bearer ${token}`
    }
    
    return config // 返回修改后的配置
  },
  (error) => {
    // 请求错误处理
    return Promise.reject(error)
  }
)

/**
 * 响应拦截器
 * 在收到响应之后执行,用于统一处理响应数据、错误处理等
 */
apiClient.interceptors.response.use(
  (response) => {
    // 只返回响应数据,忽略其他响应信息
    return response.data
  },
  (error) => {
    // 错误处理
    if (error.response?.status === 401) {
      // 401 未授权错误,清除用户登录状态
      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 登录数据,包含邮箱和密码
   * @returns 登录响应,包含用户信息和认证令牌
   */
  login: (data: LoginRequest) => {
    return apiClient.post<{ user: User; token: string }>('/auth/login', data)
  },

  /**
   * 用户注册
   * @param data 注册数据,包含用户基本信息
   * @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
// 产品 API 服务
// 设计意图:封装产品相关的 API 调用,提供类型安全的 API 服务

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

/**
 * 产品 API 服务
 * 封装产品相关的 API 调用,包括获取产品列表、获取产品详情、创建产品、更新产品、删除产品等
 */
export const productApi = {
  /**
   * 获取产品列表
   * @param params 查询参数,包含分页和筛选条件
   * @returns 产品列表
   */
  getProducts: (params?: { page?: number; limit?: number; category?: string }) => {
    return apiClient.get<Product[]>('/products', { params })
  },

  /**
   * 获取产品详情
   * @param id 产品 ID
   * @returns 产品详情
   */
  getProduct: (id: number) => {
    return apiClient.get<Product>(`/products/${id}`)
  },

  /**
   * 创建产品
   * @param data 产品数据,包含产品基本信息
   * @returns 创建的产品
   */
  createProduct: (data: CreateProductRequest) => {
    return apiClient.post<Product>('/products', data)
  },

  /**
   * 更新产品
   * @param id 产品 ID
   * @param data 产品数据,部分更新
   * @returns 更新后的产品
   */
  updateProduct: (id: number, data: UpdateProductRequest) => {
    return apiClient.put<Product>(`/products/${id}`, data)
  },

  /**
   * 删除产品
   * @param id 产品 ID
   * @returns 删除结果
   */
  deleteProduct: (id: number) => {
    return apiClient.delete(`/products/${id}`)
  }
}

5.2 API 错误处理策略

合理的错误处理策略能够提高应用的稳定性和用户体验,以下是几种常见的错误处理方式:

全局错误处理

通过 axios 响应拦截器统一处理 API 错误。

typescript 复制代码
// services/api/index.ts
// API 客户端配置文件
// 设计意图:通过响应拦截器实现全局错误处理,统一处理不同类型的 API 错误

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

/**
 * 响应拦截器
 * 统一处理 API 错误,根据错误状态码显示不同的错误提示
 */
apiClient.interceptors.response.use(
  (response) => {
    return response.data // 只返回响应数据
  },
  (error) => {
    // 处理网络错误(无响应)
    if (!error.response) {
      ElMessage.error('网络连接失败,请检查网络设置')
      return Promise.reject(error)
    }

    // 处理 401 错误(未授权)
    if (error.response.status === 401) {
      const userStore = useUserStore()
      userStore.logout() // 清除用户登录状态
      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 调用处进行错误处理,适用于需要特殊处理的场景。

typescript 复制代码
// 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) // 加载状态
  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 注册数据
   * @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, // 加载状态
    error, // 错误信息
    login, // 登录方法
    register // 注册方法
  }
}

5.3 API 数据缓存策略

合理的 API 数据缓存策略能够减少重复请求,提高应用的性能和用户体验。

内存缓存

使用内存缓存存储临时数据,适用于单次会话中的数据。

typescript 复制代码
// services/api/product.ts
// 产品 API 服务
// 设计意图:通过内存缓存减少重复请求,提高应用性能和用户体验

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

// 产品列表缓存
// 使用内存变量存储产品列表数据和缓存时间戳
let productsCache: { data: Product[]; timestamp: number } | null = null
const CACHE_DURATION = 5 * 60 * 1000 // 缓存持续时间,5 分钟

/**
 * 产品 API 服务
 */
export const productApi = {
  /**
   * 获取产品列表
   * 使用内存缓存减少重复请求,提高应用性能
   * @param params 查询参数,包含分页和筛选条件
   * @returns 产品列表
   */
  getProducts: async (params?: { page?: number; limit?: number; category?: string }) => {
    // 检查缓存是否有效
    // 如果缓存存在且未过期,直接返回缓存数据
    if (productsCache && Date.now() - productsCache.timestamp < CACHE_DURATION) {
      return productsCache.data
    }

    // 缓存无效或不存在,发起 API 请求
    const data = await apiClient.get<Product[]>('/products', { params })
    
    // 更新缓存
    // 存储响应数据和当前时间戳
    productsCache = {
      data,
      timestamp: Date.now()
    }

    return data
  },

  /**
   * 清除产品列表缓存
   * 在产品数据发生变化时调用,确保下次获取最新数据
   */
  clearProductsCache: () => {
    productsCache = null
  }
}
本地存储缓存

使用 localStorage 或 sessionStorage 存储持久化数据,适用于需要跨会话保持的数据。

typescript 复制代码
// services/api/user.ts
// 用户 API 服务
// 设计意图:通过本地存储缓存实现数据持久化,适用于需要跨会话保持的数据

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

const USER_CACHE_KEY = 'user_cache' // 本地存储的缓存键名
const CACHE_DURATION = 24 * 60 * 60 * 1000 // 缓存持续时间,24 小时

/**
 * 用户 API 服务
 */
export const userApi = {
  /**
   * 获取当前用户信息
   * 使用本地存储缓存实现数据持久化,减少重复请求
   * @returns 用户信息
   */
  getCurrentUser: async () => {
    // 检查缓存
    const cachedUser = localStorage.getItem(USER_CACHE_KEY)
    if (cachedUser) {
      const { data, timestamp } = JSON.parse(cachedUser)
      // 如果缓存存在且未过期,直接返回缓存数据
      if (Date.now() - timestamp < CACHE_DURATION) {
        return data
      }
    }

    // 缓存无效或不存在,发起 API 请求
    const data = await apiClient.get<User>('/users/me')
    
    // 更新缓存
    // 将响应数据和当前时间戳存储到本地存储
    localStorage.setItem(USER_CACHE_KEY, JSON.stringify({
      data,
      timestamp: Date.now()
    }))

    return data
  },

  /**
   * 清除用户缓存
   * 在用户信息发生变化时调用,确保下次获取最新数据
   */
  clearUserCache: () => {
    localStorage.removeItem(USER_CACHE_KEY)
  }
}

6. 工具类和公共组件

6.1 工具函数组织

工具函数是项目中常用的辅助函数,合理组织工具函数能够提高代码的复用性和可维护性。

工具函数目录结构
复制代码
src/utils/
├── format.ts          # 格式化工具
├── validation.ts      # 验证工具
├── storage.ts         # 存储工具
├── http.ts            # HTTP 工具
├── date.ts            # 日期工具
├── number.ts          # 数字工具
├── string.ts          # 字符串工具
├── array.ts           # 数组工具
└── object.ts          # 对象工具
工具函数示例
typescript 复制代码
// utils/format.ts
// 格式化工具函数
// 设计意图:提供常用的格式化功能,如金额格式化、日期格式化等

/**
 * 格式化金额
 * 将数字金额格式化为带有货币符号和千分位的字符串
 * @param amount 金额,数字类型
 * @param currency 货币符号,默认为 ¥
 * @returns 格式化后的金额字符串,如 "¥1,234.56"
 */
export function formatCurrency(amount: number, currency: string = '¥'): string {
  // 使用 toFixed(2) 保留两位小数,然后使用正则表达式添加千分位
  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) // 创建 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') // 获取秒数,并补零

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

// utils/validation.ts
// 验证工具函数
// 设计意图:提供常用的验证功能,如邮箱验证、密码强度验证等

/**
 * 验证邮箱
 * 检查邮箱地址是否符合标准格式
 * @param email 邮箱地址,字符串类型
 * @returns 是否为有效邮箱,布尔值
 */
export function isValidEmail(email: string): boolean {
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/ // 简单的邮箱格式正则表达式
  return emailRegex.test(email) // 测试邮箱地址是否匹配正则表达式
}

/**
 * 验证密码强度
 * 根据密码长度、包含的字符类型等计算密码强度等级
 * @param password 密码,字符串类型
 * @returns 密码强度等级 (0-4),数字越大表示密码强度越高
 */
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++ // 包含数字
  if (/[^A-Za-z0-9]/.test(password)) strength++ // 包含特殊字符
  return strength // 返回密码强度等级
}

// utils/storage.ts
// 存储工具函数
// 设计意图:封装本地存储操作,提供类型安全的存储工具函数

/**
 * 设置本地存储
 * 将值存储到 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 组件,合理组织公共组件能够提高开发效率和 UI 一致性。

公共组件目录结构
复制代码
src/components/
├── common/            # 通用基础组件
│   ├── Button.vue     # 按钮组件
│   ├── Input.vue      # 输入框组件
│   ├── Dialog.vue     # 对话框组件
│   └── Loading.vue    # 加载组件
├── layout/            # 布局相关组件
│   ├── Header.vue     # 头部组件
│   ├── Sidebar.vue    # 侧边栏组件
│   └── Footer.vue     # 底部组件
└── business/          # 业务相关组件
    ├── UserCard.vue   # 用户卡片组件
    ├── ProductList.vue # 产品列表组件
    └── OrderForm.vue  # 订单表单组件
公共组件设计原则
  1. 单一职责:每个组件只负责一个功能,便于理解和维护
  2. 可配置性:通过 props 提供足够的配置选项,提高组件的灵活性
  3. 可扩展性:设计合理的组件结构,便于后续功能的扩展
  4. 类型安全:充分利用 TypeScript 的类型系统,为组件定义明确的类型
  5. 文档完备:为组件提供详细的文档和使用示例
公共组件示例
vue 复制代码
<!-- components/common/Button.vue -->
<!-- 通用按钮组件 -->
<!-- 设计意图:提供一个可配置的通用按钮组件,支持多种类型、尺寸和状态 -->

<template>
  <button
    :class="[
      'btn',
      `btn-${variant}`, // 按钮类型样式
      `btn-${size}`, // 按钮尺寸样式
      { 'btn-block': block }, // 块级按钮样式
      { 'btn-disabled': disabled } // 禁用状态样式
    ]"
    :disabled="disabled" // 禁用状态
    @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
  /**
   * 是否禁用
   * 禁用状态下按钮不可点击
   * @default false
   */
  disabled?: boolean
}>()

/**
 * 按钮组件事件
 * 使用 TypeScript 类型定义,提供更好的类型提示
 */
const emit = defineEmits<{
  /**
   * 点击事件
   * 当用户点击按钮时触发
   * @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%;
}

/* 禁用状态样式 */
.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     # 节流逻辑
组合式 API 示例
typescript 复制代码
// composables/useAuth.ts
// 认证相关逻辑
// 设计意图:封装认证相关的逻辑,供多个组件复用,实现逻辑的模块化和可维护性

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

/**
 * 认证相关逻辑
 * 封装登录、注册、登出等认证操作,提供统一的认证状态管理
 * 使用 Composition API 风格,便于在组件中使用
 * @returns 认证相关的状态和方法
 */
export function useAuth() {
  const userStore = useUserStore() // 获取用户状态管理 Store
  const loading = ref(false) // 加载状态
  const error = ref<string | null>(null) // 错误信息

  /**
   * 是否已认证
   * 从用户 Store 中获取认证状态
   */
  const isAuthenticated = computed(() => userStore.isAuthenticated)

  /**
   * 当前用户
   * 从用户 Store 中获取当前用户信息
   */
  const currentUser = computed(() => userStore.currentUser)

  /**
   * 用户登录
   * 调用登录 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
      userStore.login(response.user, response.token) // 更新用户状态
      return response // 返回登录响应
    } catch (err: any) {
      // 自定义错误信息
      error.value = err.response?.data?.message || '登录失败,请检查邮箱和密码'
      throw err // 重新抛出错误
    } finally {
      loading.value = false // 结束加载
    }
  }

  /**
   * 用户注册
   * 调用注册 API,更新用户状态
   * @param userData 用户注册数据
   * @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) // 更新用户状态
      return response // 返回注册响应
    } catch (err: any) {
      // 自定义错误信息
      error.value = err.response?.data?.message || '注册失败,请稍后重试'
      throw err // 重新抛出错误
    } finally {
      loading.value = false // 结束加载
    }
  }

  /**
   * 用户登出
   * 调用登出 API,清除用户状态
   * @throws 登出失败时抛出错误
   */
  async function logout() {
    loading.value = true // 开始加载
    error.value = null // 清空错误信息
    try {
      await userApi.logout() // 调用登出 API
      userStore.logout() // 清除用户状态
    } catch (err: any) {
      // 自定义错误信息
      error.value = err.response?.data?.message || '登出失败,请稍后重试'
      throw err // 重新抛出错误
    } finally {
      loading.value = false // 结束加载
    }
  }

  /**
   * 获取当前用户信息
   * 调用获取用户信息 API,更新用户状态
   * @returns 用户信息
   * @throws 获取失败时抛出错误
   */
  async function fetchCurrentUser() {
    loading.value = true // 开始加载
    error.value = null // 清空错误信息
    try {
      const user = await userApi.getCurrentUser() // 调用获取用户信息 API
      userStore.setCurrentUser(user) // 更新用户状态
      return user // 返回用户信息
    } catch (err: any) {
      // 自定义错误信息
      error.value = err.response?.data?.message || '获取用户信息失败'
      throw err // 重新抛出错误
    } finally {
      loading.value = false // 结束加载
    }
  }

  return {
    loading, // 加载状态
    error, // 错误信息
    isAuthenticated, // 是否已认证
    currentUser, // 当前用户
    login, // 登录方法
    register, // 注册方法
    logout, // 登出方法
    fetchCurrentUser // 获取当前用户信息方法
  }
}

// composables/useApi.ts
// API 调用逻辑
// 设计意图:封装 API 请求的通用逻辑,供多个组件和 composables 复用,实现错误处理和加载状态的统一管理

import { ref } from 'vue'

/**
 * API 调用逻辑
 * 封装 API 请求的通用逻辑,包括加载状态管理和错误处理
 * 使用 Composition API 风格,便于在组件和其他 composables 中使用
 * @returns API 调用相关的状态和方法
 */
export function useApi() {
  const loading = ref(false) // 加载状态
  const error = ref<string | null>(null) // 错误信息

  /**
   * 发起 API 请求
   * 封装 API 请求的通用逻辑,处理加载状态和错误
   * @param apiCall API 调用函数,返回 Promise
   * @returns API 响应数据
   * @throws API 请求失败时抛出错误
   */
  async function request<T>(apiCall: () => Promise<T>): Promise<T> {
    loading.value = true // 开始加载
    error.value = null // 清空错误信息
    try {
      return await apiCall() // 执行 API 调用
    } catch (err: any) {
      // 自定义错误信息
      error.value = err.response?.data?.message || '请求失败,请稍后重试'
      throw err // 重新抛出错误
    } finally {
      loading.value = false // 结束加载
    }
  }

  return {
    loading, // 加载状态
    error, // 错误信息
    request // API 请求方法
  }
}

7. 企业级项目案例分析

7.1 项目架构演进

初始架构

在项目初期,通常会采用相对简单的架构,以快速实现核心功能。

复制代码
// 初始架构
// 项目初期的简单架构,以快速实现核心功能为目标
// 设计意图:在项目初期,使用简单的目录结构,快速搭建项目框架

// 特点:
// - 目录结构简单,易于理解和快速上手
// - 功能模块划分明确,便于初期开发
// - 适用于中小型项目或项目初期阶段

// 适用场景:项目初期,功能相对简单,团队规模较小
src/
├── assets/            # 静态资源(图片、样式等)
├── components/        # 公共组件
├── views/             # 页面组件
├── router/            # 路由配置
├── store/             # 状态管理(如 Vuex)
├── services/          # API 服务
├── utils/             # 工具函数
├── App.vue            # 根组件
└── main.ts            # 应用入口文件
演进架构

随着项目规模的扩大,架构会逐渐演进,添加更多的功能模块和组织结构。

复制代码
// 演进架构
// 随着项目规模扩大,架构逐渐演进,添加更多功能模块和组织结构
// 设计意图:通过完善的目录结构,提高代码的可维护性和可扩展性,适应项目的不断发展

// 特点:
// - 目录结构更加完善,功能模块划分更加细致
// - 引入了更多的 TypeScript 相关目录,如 types、enums 等
// - 使用 Pinia 替代 Vuex,采用 Composition API 风格
// - 增加了更多的功能模块,如 composables、layouts、models 等

// 适用场景:中大型项目,功能相对复杂,团队规模较大
src/
├── assets/            # 静态资源(图片、样式、图标等)
├── components/        # 公共组件(按功能或类型划分)
├── composables/       # 组合式 API 逻辑(可复用的业务逻辑)
├── constants/         # 常量定义(API 地址、路由名称等)
├── enums/             # 枚举类型(用户角色、订单状态等)
├── layouts/           # 布局组件(默认布局、认证布局等)
├── models/            # 数据模型(用户模型、产品模型等)
├── router/            # 路由配置(路由定义、守卫等)
├── services/          # 服务层(API 服务、工具服务等)
├── stores/            # Pinia 状态管理(按模块划分的状态管理)
├── types/             # TypeScript 类型定义(API 类型、组件类型等)
├── utils/             # 工具函数(格式化、验证、存储等)
├── views/             # 页面组件(按业务功能划分)
├── App.vue            # 根组件
├── main.ts            # 应用入口文件
└── env.d.ts           # 环境变量类型声明
微前端架构

对于超大型应用,可以考虑采用微前端架构,将应用拆分为多个独立的微应用。

复制代码
// 微前端架构
// 对于超大型应用,采用微前端架构,将应用拆分为多个独立的微应用
// 设计意图:通过微前端架构,实现应用的模块化和独立部署,提高开发效率和团队协作

// 特点:
// - 将应用拆分为多个独立的微应用,每个微应用可以独立开发、部署和维护
// - 主应用(shell)负责微应用的加载和管理
// - 共享资源(packages)供多个微应用复用
// - 提高了应用的可扩展性和可维护性

// 适用场景:超大型应用,多个团队负责不同业务模块,需要独立部署和维护
apps/                  # 应用目录,包含所有微应用
├── shell/             # 主应用(shell),负责微应用的加载和管理
├── auth/              # 认证微应用,处理用户登录、注册等认证功能
├── dashboard/         # 仪表盘微应用,展示用户仪表盘和概览数据
└── products/          # 产品微应用,处理产品相关的功能

packages/              # 共享资源目录,包含多个微应用共用的代码
├── components/        # 共享组件,供多个微应用复用
├── utils/             # 共享工具函数,供多个微应用复用
└── types/             # 共享类型定义,供多个微应用复用

7.2 架构优化策略

  1. 性能优化

    • 代码分割和懒加载
    • 资源压缩和缓存
    • 减少不必要的重渲染
  2. 可维护性优化

    • 模块化和组件化
    • 代码规范和命名约定
    • 文档和注释
  3. 可扩展性优化

    • 插件化架构
    • 配置驱动开发
    • 依赖注入
  4. 安全性优化

    • 认证和授权
    • 数据验证和 sanitization
    • 防止 XSS 和 CSRF 攻击

7.3 实际项目案例

电商平台

架构特点

  • 模块化设计,按业务功能划分模块
  • 微前端架构,将不同业务域拆分为独立的微应用
  • 服务端渲染,提高首屏加载速度和 SEO
  • 实时数据更新,使用 WebSocket 实现商品价格和库存的实时更新

核心模块

  • 用户认证模块:处理用户登录、注册、密码重置等
  • 商品模块:处理商品列表、详情、搜索等
  • 购物车模块:处理购物车添加、修改、结算等
  • 订单模块:处理订单创建、支付、物流等
  • 支付模块:集成多种支付方式
企业管理系统

架构特点

  • 权限管理系统,基于角色的访问控制
  • 工作流引擎,支持自定义业务流程
  • 数据可视化,提供丰富的报表和图表
  • 多语言支持,适应国际化需求

核心模块

  • 用户管理模块:处理用户信息、角色、权限等
  • 组织管理模块:处理部门、职位、员工等
  • 资产管理模块:处理资产登记、折旧、盘点等
  • 财务管理模块:处理预算、报销、审批等
  • 项目管理模块:处理项目计划、任务、进度等

8. 总结与实践

8.1 项目架构实践

  1. 合理的目录结构

    • 按照功能模块组织代码
    • 建立清晰的层次结构
    • 采用一致的命名规范
  2. 状态管理策略

    • 使用 Pinia 进行状态管理
    • 按模块划分 Store
    • 合理使用持久化存储
  3. 路由架构设计

    • 模块化路由配置
    • 合理使用路由守卫
    • 支持动态路由和权限控制
  4. API 层设计

    • 统一的 API 服务封装
    • 合理的错误处理策略
    • 有效的数据缓存机制
  5. 工具类和公共组件

    • 可复用的工具函数
    • 可配置的公共组件
    • 类型安全的组合式 API
  6. 代码质量保证

    • 严格的 TypeScript 类型检查
    • 统一的代码规范和格式化
    • 完善的测试用例

8.2 团队协作实践

  1. 代码规范

    • 制定统一的代码规范
    • 使用 ESLint 和 Prettier 进行代码检查和格式化
    • 定期进行代码审查
  2. 版本控制

    • 采用 Git 工作流
    • 合理的分支管理策略
    • 规范的提交信息
  3. 文档管理

    • 项目架构文档
    • 组件和 API 文档
    • 开发和部署指南
  4. 自动化工具

    • CI/CD 流程
    • 自动化测试
    • 代码质量检测

8.3 未来发展趋势

  1. 微前端架构

    • 将大型应用拆分为多个独立的微应用
    • 提高团队协作效率和应用可维护性
  2. Serverless 架构

    • 前端和后端都采用 Serverless 架构
    • 降低运维成本和提高扩展性
  3. AI 辅助开发

    • 使用 AI 工具辅助代码生成和优化
    • 提高开发效率和代码质量
  4. WebAssembly

    • 部分计算密集型任务使用 WebAssembly
    • 提高应用性能
  5. 边缘计算

    • 将部分计算和存储迁移到边缘节点
    • 减少延迟和提高用户体验

9. 附录

9.1 技术栈推荐

| 类别 | 技术 | 版本 | 用途 |

总结

Vue 3 + TypeScript 项目架构设计是一个持续迭代和优化的过程,需要根据项目的具体需求和团队的实际情况进行灵活调整和优化。通过合理的架构设计和实践,我们可以构建出高质量、可维护、可扩展的 Vue 3 + TypeScript 项目,为前端开发团队带来更好的开发体验和更高的生产效率。

相关推荐
萧曵 丶11 小时前
Vue 中父子组件之间最常用的业务交互场景
javascript·vue.js·交互
Amumu1213812 小时前
Vue3扩展(二)
前端·javascript·vue.js
泓博14 小时前
Android中仿照View selector自定义Compose Button
android·vue.js·elementui
牛奶14 小时前
《前端架构设计》:除了写代码,我们还得管点啥
前端·架构·设计
+VX:Fegn089514 小时前
计算机毕业设计|基于springboot + vue鲜花商城系统(源码+数据库+文档)
数据库·vue.js·spring boot·后端·课程设计
pas13615 小时前
45-mini-vue 实现代码生成三种联合类型
前端·javascript·vue.js
苏渡苇16 小时前
Java + Redis + MySQL:工业时序数据缓存与持久化实战(适配高频采集场景)
java·spring boot·redis·后端·spring·缓存·架构
麦聪聊数据16 小时前
如何用 B/S 架构解决混合云环境下的数据库连接碎片化难题?
运维·数据库·sql·安全·架构
2的n次方_16 小时前
CANN HCOMM 底层架构深度解析:异构集群通信域管理、硬件链路使能与算力重叠优化机制
架构
技术传感器16 小时前
大模型从0到精通:对齐之心 —— 人类如何教会AI“好“与“坏“ | RLHF深度解析
人工智能·深度学习·神经网络·架构