Vue3 项目的最佳工程化实践参考

介绍一下 Vue 3 项目的最佳工程化实践。以下是推荐的项目结构和关键实践:

一、TS

src/
├── main.ts # 应用入口文件
├── App.vue # 根组件
├── env.d.ts # 环境变量声明
├── assets/ # 静态资源
├── components/ # 全局通用组件
│ ├── common/ # 基础公共组件
│ └── business/ # 业务公共组件
├── composables/ # 组合式函数
├── configs/ # 配置文件
├── plugins/ # 插件配置
│ ├── pinia.ts # Pinia 配置
│ ├── router.ts # Router 配置
│ └── index.ts # 插件统一注册
├── router/ # 路由配置
│ ├── modules/ # 路由模块
│ └── index.ts # 路由入口
├── stores/ # Pinia 状态管理
│ ├── modules/ # 状态模块
│ └── index.ts # 状态入口
├── services/ # API 服务
│ ├── api/ # API 接口定义
│ └── http.ts # axios 配置
├── styles/ # 全局样式
├── types/ # TS 类型定义
├── utils/ # 工具函数
└── views/ # 页面组件

下面是各个关键部分的最佳实践示例:

1、插件配置 (src/plugins/index.ts):

import { type App } from 'vue'
import { setupPinia } from './pinia'
import { setupRouter } from './router'
// 统一注册插件
export function setupPlugins(app: App) {
setupPinia(app)
setupRouter(app)
// 其他插件配置...
}

2、Pinia 配置 (src/plugins/pinia.ts):

import { type App } from 'vue'
import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
export function setupPinia(app: App) {
const pinia = createPinia()
pinia.use(piniaPluginPersistedstate)
app.use(pinia)
}

3、HTTP 服务配置 (src/services/http.ts):

import axios from 'axios'
import type { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios'
class HttpRequest {
private instance: AxiosInstance
constructor(config: AxiosRequestConfig) {
this.instance = axios.create(config)
this.setupInterceptors()
}
private setupInterceptors() {
this.instance.interceptors.request.use(
(config) => {
// 请求拦截
return config
},
(error) => {
return Promise.reject(error)
}
)
this.instance.interceptors.response.use(
(response) => {
// 响应拦截
return response.data
},
(error) => {
return Promise.reject(error)
}
)
}
request<T = any>(config: AxiosRequestConfig): Promise<T> {
return this.instance.request(config)
}
}
export const http = new HttpRequest({
baseURL: import.meta.env.VITE_API_BASE_URL,
timeout: 10000
})

4、API 接口定义 (src/services/api/user.ts):

import { http } from '../http'
export interface UserInfo {
id: number
name: string
// ...
}
export const userApi = {
getUserInfo(id: number) {
return http.request<UserInfo>({
url: `/user/${id}`,
method: 'GET'
})
}
}

5、Store 模块 (src/stores/modules/user.ts):

import { defineStore } from 'pinia'
import type { UserInfo } from '@/services/api/user'
export const useUserStore = defineStore('user', {
state: () => ({
userInfo: null as UserInfo | null
}),
actions: {
async fetchUserInfo(id: number) {
const { userApi } = await import('@/services/api/user')
this.userInfo = await userApi.getUserInfo(id)
}
},
persist: true
})

6、入口文件 (src/main.ts):

import { createApp } from 'vue'
import App from './App.vue'
import { setupPlugins } from './plugins'
import '@/styles/index.scss'
const app = createApp(App)
setupPlugins(app)
app.mount('#app')

7、环境变量配置 (.env.development):

VITE_API_BASE_URL=http://localhost:3000

8、组合式函数 (src/composables/useRequest.ts):

import { ref } from 'vue'
export function useRequest<T, P extends any[]>(
api: (...args: P) => Promise<T>
) {
const data = ref<T | null>(null)
const loading = ref(false)
const error = ref<Error | null>(null)
const execute = async (...args: P) => {
loading.value = true
error.value = null
try {
data.value = await api(...args)
} catch (e) {
error.value = e as Error
} finally {
loading.value = false
}
}
return {
data,
loading,
error,
execute
}
}

这样的工程化实践有以下优点:

  1. 目录结构清晰,职责分明
  2. 模块化设计,便于维护和扩展
  3. 类型安全,使用 TypeScript
  4. 统一的错误处理和状态管理
  5. 可复用的组合式函数
  6. 环境配置分离
  7. API 接口统一管理
  8. 插件配置集中管理

此外,还建议:

  1. 使用 ESLint + Prettier 统一代码风格
  2. 使用 Husky + lint-staged 进行提交前检查
  3. 使用 Conventional Commits 规范提交信息
  4. 编写单元测试确保代码质量
  5. 使用 CI/CD 实现自动化部署

这样的工程化结构可以帮助团队更好地协作,提高代码质量和开发效率。

二、JS

对于 JavaScript 项目,我们同样可以实现良好的工程化。以下是基于 JavaScript 的最佳实践:

src/
├── main.js # 应用入口文件
├── App.vue # 根组件
├── assets/ # 静态资源
├── components/ # 全局通用组件
│ ├── common/ # 基础公共组件
│ └── business/ # 业务公共组件
├── hooks/ # 组合式函数 (composables)
├── configs/ # 配置文件
├── plugins/ # 插件配置
├── router/ # 路由配置
├── stores/ # Pinia 状态管理
├── services/ # API 服务
├── styles/ # 全局样式
└── utils/ # 工具函数

下面是各个核心文件的示例代码:

1、插件配置 (src/plugins/index.js):

import { setupPinia } from './pinia'
import { setupRouter } from './router'
export function setupPlugins(app) {
setupPinia(app)
setupRouter(app)
}

2、Pinia 配置 (src/plugins/pinia.js):

import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
export function setupPinia(app) {
const pinia = createPinia()
pinia.use(piniaPluginPersistedstate)
app.use(pinia)
}

3、HTTP 服务配置 (src/services/http.js):

import axios from 'axios'
class HttpRequest {
constructor(config) {
this.instance = axios.create(config)
this.setupInterceptors()
}
setupInterceptors() {
// 请求拦截器
this.instance.interceptors.request.use(
config => {
// 在发送请求之前做些什么
const token = localStorage.getItem('token')
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
return config
},
error => {
return Promise.reject(error)
}
)
// 响应拦截器
this.instance.interceptors.response.use(
response => {
// 对响应数据做点什么
const { code, data, message } = response.data
if (code === 200) {
return data
} else {
// 统一错误处理
this.handleError(code, message)
return Promise.reject(new Error(message))
}
},
error => {
// 对响应错误做点什么
const { status } = error.response
this.handleError(status)
return Promise.reject(error)
}
)
}
handleError(code, message) {
switch (code) {
case 401:
// 未登录或 token 过期
break
case 403:
// 无权限
break
case 404:
// 请求的资源不存在
break
case 500:
// 服务器错误
break
default:
// 其他错误
break
}
}
request(config) {
return this.instance.request(config)
}
}
export const http = new HttpRequest({
baseURL: import.meta.env.VITE_API_BASE_URL,
timeout: 10000
})

4、API 接口定义 (src/services/api/user.js):

import { http } from '../http'
export const userApi = {
// 获取用户信息
getUserInfo(id) {
return http.request({
url: `/user/${id}`,
method: 'GET'
})
},
// 用户登录
login(data) {
return http.request({
url: '/user/login',
method: 'POST',
data
})
}
}

5、Store 模块 (src/stores/modules/user.js):

import { defineStore } from 'pinia'
import { userApi } from '@/services/api/user'
export const useUserStore = defineStore('user', {
state: () => ({
userInfo: null,
token: localStorage.getItem('token') || ''
}),
getters: {
isLogin: state => !!state.token
},
actions: {
async login(credentials) {
try {
const { token, userInfo } = await userApi.login(credentials)
this.token = token
this.userInfo = userInfo
localStorage.setItem('token', token)
return true
} catch (error) {
return false
}
},
async getUserInfo(id) {
try {
this.userInfo = await userApi.getUserInfo(id)
} catch (error) {
console.error('获取用户信息失败:', error)
}
},
logout() {
this.token = ''
this.userInfo = null
localStorage.removeItem('token')
}
},
persist: {
key: 'user-store',
paths: ['token']
}
})

6、组合式函数 (src/hooks/useRequest.js):

import { ref } from 'vue'
export function useRequest(api) {
const data = ref(null)
const loading = ref(false)
const error = ref(null)
const execute = async (...args) => {
loading.value = true
error.value = null
try {
data.value = await api(...args)
} catch (err) {
error.value = err
console.error('请求错误:', err)
} finally {
loading.value = false
}
}
return {
data,
loading,
error,
execute
}
}

7、入口文件 (src/main.js):

import { createApp } from 'vue'
import App from './App.vue'
import { setupPlugins } from './plugins'
import './styles/index.scss'
const app = createApp(App)
setupPlugins(app)
app.mount('#app')

8、工具函数 (src/utils/validate.js):

// 表单验证工具函数
export const validators = {
isEmail(value) {
return /^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$/.test(value)
},
isPhone(value) {
return /^1[3-9]\d{9}$/.test(value)
},
isPassword(value) {
return /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[a-zA-Z\d]{8,}$/.test(value)
}
}

推荐的工程化实践:

1、使用 ESLint 和 Prettier:

// .eslintrc.js
module.exports = {
root: true,
env: {
node: true,
browser: true,
es2021: true
},
extends: [
'plugin:vue/vue3-recommended',
'eslint:recommended'
],
rules: {
// 自定义规则
}
}

2、使用 husky 和 lint-staged 进行代码检查:

{
"scripts": {
"lint": "eslint --ext .js,.vue src",
"lint:fix": "eslint --ext .js,.vue src --fix"
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"*.{js,vue}": [
"eslint --fix",
"git add"
]
}
}

4、统一的错误处理:

// src/utils/error.js
export class BusinessError extends Error {
constructor(code, message) {
super(message)
this.code = code
}
}
export const errorHandler = (error) => {
if (error instanceof BusinessError) {
// 处理业务错误
} else {
// 处理其他错误
}
}

主要优势:

  1. 代码组织清晰,易于维护
  2. 统一的错误处理机制
  3. 可重用的工具函数和 hooks
  4. 规范的代码风格
  5. API 接口统一管理
  6. 状态管理清晰
  7. 自动化的代码检查和格式化

最后,建议:

  1. 合理使用注释,特别是复杂逻辑部分
  2. 遵循一致的命名规范
  3. 适当做性能优化
  4. 定期进行代码审查
  5. 保持依赖包的更新
posted @   非法关键字  阅读(155)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 通过 API 将Deepseek响应流式内容输出到前端
· AI Agent开发,如何调用三方的API Function,是通过提示词来发起调用的吗
点击右上角即可分享
微信分享提示