Vue3 + Element Plus + Vite 企业级后台框架搭建全流程

📅 2026/6/27 6:22:17
Vue3 + Element Plus + Vite 企业级后台框架搭建全流程
项目写了三个月目录乱成一锅粥找个组件要翻半天——问题不在你在一开始没搭好架子。这篇文章给你一套拿来即用的企业级框架模板。一、前言很多开发者拿到 Vue3 之后第一反应是npm create vuelatest跑起来就开干了。但企业级项目不是 Demo如果开局没定好规矩写到第三个月会发现目录结构一团乱找个组件要翻半天ESLint 和同事的不一致提交一次冲突一次环境变量东一个西一个打包上线才发现接口地址配错了这篇文章带你从零搭建一套可落地的企业级 Vue3 后台框架——不是玩具项目而是我过去一年在生产环境中迭代出来的最佳实践。二、技术选型与版本踩坑2.1 选型理由技术版本选型理由Vue 33.5.xComposition API script setup是未来Vue2 已经不推荐新项目使用Vite6.x开发服务器秒启动HMR 极快Webpack 项目迁移过来开发体验提升明显Element Plus2.9.x中文生态最好的 Vue3 UI 库企业级组件覆盖完整TypeScript5.x类型安全是多人协作的生命线不解释了Pinia2.xVuex 的官方替代TS 友好API 简洁Vue Router4.xVue3 配套支持组合式 API 路由守卫unplugin-auto-import0.18.x自动导入 Vue/Element Plus API告别重复 importunplugin-vue-components0.28.xElement Plus 组件按需导入打包体积减少 60%2.2 一个我踩了两天的坑Element Plus 版本兼容性问题去年我把项目从 Element Plus 2.2 升到 2.9部分组件的v-model绑定方式变了表单验证规则也改了。升级前务必读一遍 CHANGELOG尤其是 Breaking Changes 部分。经验之谈锁定依赖版本号别用^前缀。element-plus: 2.9.7比^2.9.7安全得多。三、项目初始化3.1 创建项目# 使用官方脚手架创建 npm create vuelatest admin-framework # 选项配置 # ✔ TypeScript? … Yes # ✔ JSX Support? … No个人偏好看团队 # ✔ Vue Router? … Yes # ✔ Pinia? … Yes # ✔ Vitest? … Yes # ✔ ESLint? … Yes # ✔ Prettier? … Yes cd admin-framework npm install3.2 安装企业级依赖# UI 框架 自动导入 npm install element-plus element-plus/icons-vue # 自动导入插件开发依赖 npm install -D unplugin-auto-import unplugin-vue-components # 工具库 npm install axios dayjs nprogress # 类型声明 npm install -D types/node types/nprogress3.3 Vite 配置核心// vite.config.ts import { defineConfig } from vite import vue from vitejs/plugin-vue import { fileURLToPath, URL } from node:url import AutoImport from unplugin-auto-import/vite import Components from unplugin-vue-components/vite import { ElementPlusResolver } from unplugin-vue-components/resolvers export default defineConfig({ plugins: [ vue(), AutoImport({ resolvers: [ElementPlusResolver()], imports: [vue, vue-router, pinia], dts: types/auto-imports.d.ts // 自动生成类型声明 }), Components({ resolvers: [ElementPlusResolver()], dts: types/components.d.ts }) ], resolve: { alias: { : fileURLToPath(new URL(./src, import.meta.url)) } }, server: { port: 80, host: 0.0.0.0, proxy: { /api: { target: http://localhost:8080, changeOrigin: true, rewrite: (path) path.replace(/^\/api/, ) }, /file: { target: http://localhost:9300, changeOrigin: true } } }, build: { outDir: dist, assetsDir: static, chunkSizeWarningLimit: 2000, // 若依框架页面多调大阈值 rollupOptions: { output: { manualChunks: { element-plus: [element-plus], vendor: [vue, vue-router, pinia] } } } } })关键配置说明manualChunks把 Element Plus、Vue 核心单独拆包首页加载体积从 2MB 降到 800KBproxy的/file文件上传服务通常单独部署这里做了独立代理四、目录结构规范这是我在若依框架基础上演进出的目录结构经过 3 个项目验证src/ ├── api/ # API 接口层 │ ├── system/ # 系统管理模块 │ │ ├── user.ts │ │ ├── role.ts │ │ └── menu.ts │ └── business/ # 业务模块按需扩展 │ └── order.ts │ ├── assets/ # 静态资源 │ ├── images/ # 图片 │ └── styles/ # 全局样式 │ ├── variables.scss # SCSS 变量 │ ├── element.scss # Element Plus 样式覆盖 │ └── index.scss # 全局样式入口 │ ├── components/ # 公共组件 │ ├── FileUpload/ # 文件上传 │ ├── IconSelect/ # 图标选择器 │ ├── ImagePreview/ # 图片预览 │ └── ... │ ├── composables/ # 组合式函数hooks │ ├── useAuth.ts # 认证逻辑 │ ├── usePermission.ts # 权限判断 │ └── useTable.ts # 表格通用逻辑 │ ├── directives/ # 自定义指令 │ ├── permission.ts # v-hasPermi 按钮权限 │ └── debounce.ts # v-debounce 防抖 │ ├── layout/ # 布局组件 │ ├── index.vue # 主布局 │ ├── Sidebar/ # 侧边栏 │ ├── Navbar/ # 顶部导航 │ └── TagsView/ # 页签导航 │ ├── router/ # 路由配置 │ ├── index.ts # 路由入口 │ └── modules/ # 按模块拆分路由 │ ├── system.ts │ └── business.ts │ ├── store/ # Pinia 状态管理 │ ├── modules/ │ │ ├── user.ts # 用户状态 │ │ ├── app.ts # 应用配置 │ │ └── permission.ts # 权限状态 │ └── index.ts │ ├── utils/ # 工具函数 │ ├── request.ts # Axios 封装 │ ├── auth.ts # Token 管理 │ └── index.ts # 通用工具 │ ├── views/ # 页面视图 │ ├── system/ # 系统管理 │ └── business/ # 业务页面 │ ├── App.vue ├── main.ts └── permission.ts # 路由守卫全局三条铁律api/目录必须和views/一一对应——找接口和找页面路径一致降低心智负担components/只放纯公共组件——业务组件放各自views/下的components/子目录**超过 3 个路由模块就拆modules/**——一个index.ts堆 50 个路由是灾难五、环境变量与多环境配置5.1 环境文件规划.env # 所有环境共享 .env.development # 开发环境 .env.production # 生产环境 .env.staging # 预发布环境可选5.2 配置内容# .env.development VITE_APP_TITLE 系统管理(开发) VITE_APP_BASE_API /api VITE_APP_FILE_URL /file VITE_APP_ENV development # .env.production VITE_APP_TITLE MqCode VITE_APP_BASE_API /prod-api VITE_APP_FILE_URL /file VITE_APP_ENV production// types/env.d.ts - 为环境变量提供类型提示 /// reference typesvite/client / interface ImportMetaEnv { readonly VITE_APP_TITLE: string readonly VITE_APP_BASE_API: string readonly VITE_APP_FILE_URL: string readonly VITE_APP_ENV: development | production | staging } interface ImportMeta { readonly env: ImportMetaEnv }5.3 Axios 请求封装// src/utils/request.ts import axios, { type AxiosInstance, type InternalAxiosRequestConfig } from axios import { ElMessage, ElMessageBox } from element-plus import { getToken } from ./auth const service: AxiosInstance axios.create({ baseURL: import.meta.env.VITE_APP_BASE_API, timeout: 30000, headers: { Content-Type: application/json;charsetutf-8 } }) // 请求拦截器 service.interceptors.request.use( (config: InternalAxiosRequestConfig) { const token getToken() if (token) { config.headers.Authorization Bearer ${token} } return config }, (error) Promise.reject(error) ) // 响应拦截器 service.interceptors.response.use( (response) { const { code, msg, data } response.data if (code 200) return data // Token 过期 if (code 401) { ElMessageBox.confirm(登录状态已过期请重新登录, 系统提示, { confirmButtonText: 重新登录, cancelButtonText: 取消, type: warning }).then(() { location.href /login }) return Promise.reject(new Error(msg)) } ElMessage.error(msg || 请求失败) return Promise.reject(new Error(msg)) }, (error) { const message error.response?.data?.msg || error.message || 网络异常 ElMessage.error(message) return Promise.reject(error) } ) export default service三个关键设计决策**response.data直接返回data**业务代码不需要每次都写.data.data减少嵌套401 不直接跳转弹窗提示用户避免正在填写表单时被强制跳转导致数据丢失timeout设为 30s企业级文件上传可能需要较长时间太短容易误报超时六、代码规范6.1 ESLint Prettier重要提醒ESLint 9 的配置格式是扁平化的eslint.config.js和旧版.eslintrc完全不同。如果你用的是若依框架的 ESLint 8 配置别直接复制到新项目。# 安装Vue 脚手架已自带确认一下 npm install -D eslint eslint/js typescript-eslint eslint-plugin-vue prettier eslint-config-prettier// eslint.config.jsESLint 9 扁平化配置 import js from eslint/js import tseslint from typescript-eslint import pluginVue from eslint-plugin-vue import prettierConfig from eslint-config-prettier export default [ js.configs.recommended, ...tseslint.configs.recommended, ...pluginVue.configs[flat/recommended], prettierConfig, { files: [**/*.{ts,vue}], languageOptions: { parserOptions: { parser: tseslint.parser } } }, { ignores: [dist/**, node_modules/**, types/**] } ]// .prettierrc { semi: false, singleQuote: true, trailingComma: none, printWidth: 100, tabWidth: 2, arrowParens: avoid, endOfLine: auto }6.2 Git 提交规范npm install -D commitlint/cli commitlint/config-conventional npm install -D husky lint-staged// commitlint.config.js export default { extends: [commitlint/config-conventional], rules: { type-enum: [2, always, [ feat, // 新功能 fix, // 修复 docs, // 文档 style, // 格式 refactor, // 重构 perf, // 性能优化 test, // 测试 chore, // 构建/工具 revert // 回滚 ]] } }提交格式示例feat: 新增生产工单列表页面/fix: 修复质检模板保存失败的问题七、启动模板的完整 main.ts// src/main.ts import { createApp } from vue import App from ./App.vue import router from ./router import pinia from ./store // Element Plus 样式 import element-plus/dist/index.css // 若依框架自定义样式如果有的话 import /assets/styles/index.scss // 自定义指令 import permission from /directives/permission // 路由权限守卫 import ./permission const app createApp(App) app.use(router) app.use(pinia) app.directive(hasPermi, permission) app.mount(#app)main.ts坚持一个小原则每一行 import 只做一件事不混在一起。这样六个月后回来看代码一眼就知道初始化流程。八、踩坑记录坑现象原因解决方案Element Plus 样式丢失页面空白/组件无样式unplugin-vue-components未配置importStyle: cssElementPlusResolver({ importStyle: css })Vite proxy 不生效接口请求 404路径重写规则不对打开浏览器 Network 面板确认请求 URL 是否被正确代理SCSS 变量全局引入失败组件中用了$primary-color报错Vite 需要配置css.preprocessorOptionsadditionalData: use /assets/styles/variables.scss as *;TypeScript 找不到路径别名import from /api报红未配置tsconfig.json的paths见下方配置// tsconfig.json 补充追加到 compilerOptions { compilerOptions: { baseUrl: ., paths: { /*: [src/*] } } }九、总结这套模板的特点是不追求炫技只追求稳Vite unplugin 自动导入开发体验流畅打包体积可控目录结构按模块划分api/和views/一一对应降低协作成本Axios 封装一次到位Token 刷新、统一错误处理、接口类型安全环境变量 多环境切换告别打包前手动改地址的噩梦ESLint Prettier Commitlint 三板斧代码风格一致提交记录可读如果你也在独立开发产品或者对制造业数字化感兴趣欢迎关注这个公众号。我会持续分享从代码到产品的全过程——包括成功的经验也包括踩过的坑。一个人的产品之路不孤单。原创作者 MqCode全栈开发者印刷包装行业 MESCRM 系统独立开发欢迎自由转发。