本文作者:LIO
一、项目背景与技术栈选型
这是一个面向中大型后台管理系统的 Vue3 工程化全套方案。你可以直接用于项目脚手架、面试准备或技术博客。
核心技术栈
- Vue3(Composition API)
- Vite
- TypeScript
- Pinia(替代 Vuex)
- Vue Router 4
- Element Plus
- Axios 封装
- ESLint + Prettier + Husky
- Scss / CSS Variables
推荐目录结构(可直接复制使用)
src
├── api # 接口请求统一封装
├── assets # 图片、样式资源
├── components # 公共组件
├── config # 全局常量、环境变量
├── hooks # 通用 hooks
├── layouts # 布局组件
├── router # 路由 + 权限路由
├── store # Pinia 模块化仓库
├── types // TS 类型声明
├── utils // 工具函数
├── views // 业务页面
├── App.vue
└── main.ts
二、Axios 全局请求封装(生产级)
1. 创建 src/utils/request.ts
import axios from 'axios'
import { ElMessage, ElLoading } from 'element-plus'
let loadingInstance: any = null
const request = axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL,
timeout: 10000,
})
// 请求拦截器
request.interceptors.request.use(
(config) => {
loadingInstance = ElLoading.service({ fullscreen: true })
const token = localStorage.getItem('token')
if (token) config.headers.Authorization = `Bearer ${token}`
return config
},
(err) => Promise.reject(err)
)
// 响应拦截器
request.interceptors.response.use(
(res) => {
loadingInstance.close()
return res.data
},
(err) => {
loadingInstance.close()
ElMessage.error(err.response?.data?.message || '请求失败')
return Promise.reject(err)
}
)
export default request
2. API 分层示例(src/api/user.ts)
import request from '@/utils/request'
export function getUserInfo() {
return request.get('/user/info')
}
export function login(data: any) {
return request.post('/login', data)
}
三、Pinia 仓库模块化设计(src/store/index.ts)
import { defineStore } from 'pinia'
export const useUserStore = defineStore('user', {
state: () => ({
token: localStorage.getItem('token') || '',
userInfo: null as any,
}),
actions: {
setToken(token: string) {
this.token = token
localStorage.setItem('token', token)
},
async fetchUserInfo() {
const res = await getUserInfo()
this.userInfo = res.data
},
},
})
四、Vue Router + 权限路由
src/router/index.ts
import { createRouter, createWebHistory } from 'vue-router'
import { useUserStore } from '@/store/user'
const routes = [
{
path: '/',
component: () => import('@/layouts/MainLayout.vue'),
meta: { requireAuth: true },
children: [
{ path: '', component: () => import('@/views/Home.vue') },
{ path: 'user', component: () => import('@/views/User.vue') },
],
},
{ path: '/login', component: () => import('@/views/Login.vue') },
]
const router = createRouter({
history: createWebHistory(),
routes,
})
// 全局路由守卫
router.beforeEach((to, from, next) => {
const userStore = useUserStore()
if (to.meta.requireAuth && !userStore.token) {
next('/login')
} else {
next()
}
})
export default router
五、工程化与打包优化(重点)
- 路由懒加载
component: () => import('@/views/Home.vue')
- 分包策略(vite.config.ts)
export default defineConfig({
build: {
rollupOptions: {
output: {
chunkFileNames: 'js/[name]-[hash].js',
entryFileNames: 'js/[name]-[hash].js',
assetFileNames: '[ext]/[name]-[hash].[ext]',
},
},
},
})
- ESLint + Prettier
npx eslint --fix .
- Husky 提交校验
npx husky install
npx husky add .husky/pre-commit "npm lint"
六、常见业务场景落地(可直接复制)
1. 分页列表
<template>
<el-table
:data="list"
:loading="loading"
>
<!-- 列 -->
</el-table>
<el-pagination
v-model:page-size="pageSize"
v-model:current-page="pageNum"
:total="total"
@change="fetchList"
/>
</template>
2. 表单校验
const ruleRef = ref()
const rules = {
name: [{ required: true, message: '请输入名称', trigger: 'blur' }],
}
七、面试高频考点(掘金博客可直接写)
- Vue3 响应式原理(Proxy)
- Composition API 与 Options API 区别
- Pinia 与 Vuex 的差异
- Vue Router 动态路由实现
- Vite 与 Webpack 对比
- 组件懒加载、代码分割
- 前端鉴权方案(JWT + 路由守卫)
——个人观点 · 仅供参考——