ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Vue3 + Vite + Element Plus 完整接入指南:从搭建到优化

Vue3 + Vite + Element Plus 完整接入指南:从搭建到优化 1. 先用 Vite 把 Vue3 工程搭起来很多朋友从 Vue2 的 element-ui 迁到 Vue3 项目时第一反应是npm i element-ui -S结果编译直接给你抛一堆版本不兼容的红色报错。Element Plus 才是官方给 Vue3 出的组件库API 和内部实现几乎重写了一遍不能把它当成 element-ui 的简单升级版来用。这篇我直接梳理一遍 Vue3 项目接入 Element Plus 的完整流程从建工程、引组件、调主题、做国际化到排那些真正让人头疼的报错一次性讲透。接 Element Plus 之前工程得先立起来。这里我默认你用的是 Vite而不是传统的 Vue CLI 或者 Webpack 手搓配置。如果你是从 Vue2 时代一路过来的可能对 Vue CLI 比较熟但 Vue3 生态现在基本已全面倒向 Vite原因后面细说。1.1 项目初始化与 Node 版本确认先检查 Node 环境。Element Plus 官方对 Node 版本有要求太老的版本会直接导致依赖安装失败或运行时报语法错误。建议 Node 版本不低于 16稳妥一点直接上 Node 18 LTS 或 20 LTS。终端里跑一条命令确认node -v如果版本太低先去 Node 官网或者用 nvm 切到新版本这一步不做好后面装什么依赖都容易出幺蛾子。创建项目用 Vite 官方脚手架npm create vitelatest my-vue3-app -- --template vue-ts想用纯 JavaScript 就把模板换成vue但我个人建议直接用vue-tsTypeScript 在 Vue3 项目里已经是事实标准Element Plus 的类型提示对开发效率的提升非常明显。创建完成之后cd my-vue3-app npm install npm run dev浏览器打开http://localhost:5173能看到 Vite 默认的 Vue3 启动页工程就算立住了。1.2 为什么要绕开 Vue CLI 直接用 ViteVue CLI 是基于 Webpack 的冷启动一个中型项目经常要等十几秒甚至更久改一行代码触发热更新快的时候也要两三秒。Vite 开发服务器走的是原生 ES Module按需编译启动基本是秒开热更新也是毫秒级刷新。还有一个很现实的原因Element Plus 官方文档里给的示例、社区里若依和 JeecgBoot 的 Vue3 前端版本、绝大多数的 Vue3 开源后台模板默认都是 Vite。你还拿 Vue CLI 建工程照着官方文档抄配置的时候很可能会因为工具链不一致踩一堆莫名其妙的坑。新人就别走弯路了直接 Vite。1.3 工程目录里先确认三件事项目初始化完别急着装组件库先检查三个文件src/main.ts应用入口后面 Element Plus 要在这里注册src/vite-env.d.tsVite 的类型声明文件TS 项目里全局类型经常要在这个文件补充vite.config.tsVite 配置文件按需引入插件、路径别名都在这块配。路径别名建议顺手配好后端管理系统项目结构一复杂/components这种写法是刚需。在vite.config.ts里加import { defineConfig } from vite import vue from vitejs/plugin-vue import { fileURLToPath, URL } from node:url export default defineConfig({ plugins: [vue()], resolve: { alias: { : fileURLToPath(new URL(./src, import.meta.url)) } } })同时在tsconfig.json的compilerOptions里补上{ compilerOptions: { baseUrl: ., paths: { /*: [src/*] } } }这两步配好后面 import 路径能少写很多../../也避免引入 Element Plus 组件时路径层次一深把自己绕晕。2. 接入 Element Plus两种引入方式怎么选工程就绪之后核心环节来了——把 Element Plus 接进来。官方提供了两种方式完整引入和自动按需引入。这里我建议无脑选自动按需引入。刚接触的人可能觉得完整引入省事三行代码搞定确实小项目或者原型 Demo 这么干没问题但项目一上规模组件库全量打包的体积就是肉眼可见的负担。自动按需引入看起来要多配几个插件实际上也就十分钟的事一劳永逸。2.1 最省事的完整引入完整引入的方式非常简单适合急着跑通 demo 或者内部工具类小项目。先安装依赖npm install element-plus然后在main.ts里全量注册import { createApp } from vue import ElementPlus from element-plus import element-plus/dist/index.css import App from ./App.vue const app createApp(App) app.use(ElementPlus) app.mount(#app)这样做的好处是模板里可以直接用全部组件ElMessage、ElMessageBox这些命令式 API 也不需要额外引入。代价是打包体积大按需引入技术成熟之前很多项目就是这么过来的。2.2 官方推荐的自动按需引入自动按需引入依赖两个 Vite 插件unplugin-vue-components和unplugin-auto-import。前者负责组件按需加载后者负责 API 按需加载比如ElMessage、ElNotification这些函数式调用。安装npm install element-plus npm install -D unplugin-vue-components unplugin-auto-import然后在vite.config.ts里配置import { defineConfig } from vite import vue from vitejs/plugin-vue 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()] }), Components({ resolvers: [ElementPlusResolver()] }) ] })做完这一步模板里的el-button、el-table这些组件就不再需要手动import直接用。ElMessage(操作成功)这种 API 也不需要手动引入插件会自动帮你处理。就使用体验而言开发时几乎感觉不到插件的存在但最终打包出来只包含实际用到的组件代码体积能小一半以上。2.3 两种方式的对比与选择建议这里直接给一张对比表看得更清楚对比项完整引入自动按需引入配置成本极低三行代码中等需装两个插件并配置 resolver打包体积全量引入所有组件体积大只打包用到的组件和样式体积小开发体验模板里随便用API 全局可用同样随便用插件自动导入TS 类型提示需要额外配置全局类型插件自动生成类型声明提示完善推荐场景原型、Demo、小型内部系统正式项目、后台管理系统、长期维护项目选择建议很简单接正式项目就直接用自动按需引入别犹豫。完整引入的方式知道有这回事就行面试时能说清楚两者差异实际项目里用自动按需即可。2.4 手动按需引入作为补充方案介于两者之间还有一种方式——手动按需引入。比如在某个页面里script setup langts import { ElButton } from element-plus import element-plus/es/components/button/style/css /script组件和对应样式都要分别引入代码非常啰嗦而且很容易漏掉样式文件。这种方式现在已经基本被 unplugin 的方案淘汰了知道有这回事遇到特殊场景比如某个组件和插件的解析器不兼容可以兜底用一下。3. 从按钮到数据表格后台系统里的高频组件实战引入方式定下来之后真正的工作才刚开始。一个后台管理系统里用量最大的组件不外乎表单、表格、弹窗、消息提示这几个。Element Plus 的 API 和 Vue2 时代有明显差异直接照搬老代码会踩坑。3.1 表单验证的几个关键写法el-form的表单验证关键是model、rules、prop三者要配对。新手最容易犯的错是el-form上写了rulesel-form-item上忘了写prop结果验证规则死活不触发。标准写法template el-form refformRef :modelformData :rulesrules label-width100px el-form-item label用户名 propusername el-input v-modelformData.username placeholder请输入用户名 / /el-form-item el-form-item label密码 proppassword el-input v-modelformData.password typepassword placeholder请输入密码 / /el-form-item /el-form /template script setup langts import { reactive, ref } from vue import { ElMessage, type FormInstance, type FormRules } from element-plus const formRef refFormInstance() const formData reactive({ username: , password: }) const rules: FormRules { username: [ { required: true, message: 请输入用户名, trigger: blur }, { min: 3, max: 20, message: 长度在 3 到 20 个字符, trigger: blur } ], password: [ { required: true, message: 请输入密码, trigger: blur }, { min: 6, max: 32, message: 长度在 6 到 32 个字符, trigger: blur } ] } const handleSubmit async () { if (!formRef.value) return const valid await formRef.value.validate().catch(() false) if (!valid) return ElMessage.success(提交成功) } /script这里有个细节值得注意validate()返回的是一个 Promise直接await它成功了再往下走比用回调函数清晰得多。老代码里常见的validate(valid {...})回调写法在 Element Plus 里虽然也能用但结合async/await会让逻辑扁平不少。自定义校验函数也是高频需求比如校验手机号、身份证、URL 格式const rules: FormRules { phone: [ { required: true, message: 请输入手机号, trigger: blur }, { validator: (_rule, value: string, callback) { if (!/^1[3-9]\d{9}$/.test(value)) { callback(new Error(手机号格式不正确)) } else { callback() } }, trigger: blur } ] }3.2 表格列表页的模板套路后台管理系统的列表页el-table是绝对的主角。Element Plus 的表格用法和 element-ui 大体一致但插槽的写法有变化新版统一用#default具名插槽。带操作列的典型写法template el-table :datatableData border stripe v-loadingloading el-table-column propname label名称 min-width140 / el-table-column propcategory label分类 min-width100 / el-table-column label状态 width100 template #default{ row } el-tag :typerow.status 1 ? success : danger {{ row.status 1 ? 启用 : 停用 }} /el-tag /template /el-table-column el-table-column label创建时间 min-width180 template #default{ row } {{ formatDate(row.createTime) }} /template /el-table-column el-table-column label操作 width180 fixedright template #default{ row } el-button link typeprimary clickhandleEdit(row)编辑/el-button el-button link typedanger clickhandleDelete(row)删除/el-button /template /el-table-column /el-table /template几个容易踩的细节el-table-column的prop要和数据字段名严格对应嵌套字段需要自己把值算好或者用 formatter操作列加fixedright列多了才不会被挤到屏幕外面v-loading指令是 Element Plus 提供的配合表格加载状态非常顺手不用额外引入直接用给表格加border和stripe属性视觉上更有层次感列表一长就不容易看串行。3.3 弹窗和消息提示的正确姿势消息提示是反馈操作的灵魂。Element Plus 里ElMessage和ElMessageBox的使用方式有两种一种是在组件里直接引入调用另一种是通过全局属性调用。推荐第一种script setup langts import { ElMessage, ElMessageBox } from element-plus const handleDelete (row: any) { ElMessageBox.confirm(确定要删除「${row.name}」吗, 提示, { confirmButtonText: 确定, cancelButtonText: 取消, type: warning }) .then(() { ElMessage.success(删除成功) }) .catch(() {}) } /script注意ElMessageBox.confirm的catch一定要接住用户点取消时 Promise 会 reject不处理的话控制台会报 Unhandled Promise Rejection 警告。另一个常见坑是app.config.globalProperties.$message这种方式。虽然在main.ts里注册完每个组件里都能用但配合script setup时全局属性不会被自动暴露还得用getCurrentInstance去取类型还不好写远不如import { ElMessage }来得清爽。除非维护老项目否则不建议把命令式 API 挂全局。3.4 修改 tabs 标签页样式这类“组件内部样式”问题后台系统中 tabs 非常常见但 Element Plus 默认样式不一定符合设计要求。比如要改激活标签的下划线颜色直接写样式经常不生效——因为组件内部用了深度选择器作用域隔离普通类名覆盖不到。用:deep()处理style scoped langscss .tabs-wrapper { :deep(.el-tabs__item.is-active) { color: #409eff; font-weight: 600; } :deep(.el-tabs__active-bar) { background-color: #409eff; height: 3px; border-radius: 2px; } } /style:deep()的本质是让父级选择器穿透到子组件的内部节点理解这一点后修改任何第三方组件库的样式都不再是玄学。优先看控制台里最终渲染出的类名和 DOM 结构再决定要覆盖哪个选择器。4. 布局、主题定制与暗黑模式后台管理系统跑起来之后接下来要解决的是“长得像个正经系统”的问题。Element Plus 的布局组件能解决骨架搭建主题定制解决视觉风格暗黑模式则是现在很多管理系统的标配需求。4.1 el-container 搭后台基本框架用el-container、el-aside、el-header、el-main组合出后台经典布局左侧菜单、顶部导航、右侧内容区。template el-container classlayout-wrapper el-aside :widthisCollapse ? 64px : 220px el-menu :collapseisCollapse :collapse-transitionfalse router :default-activeroute.path el-menu-item index/dashboard el-iconHomeFilled //el-icon span仪表盘/span /el-menu-item el-sub-menu index1 template #title el-iconSetting //el-icon span系统管理/span /template el-menu-item index/system/user用户管理/el-menu-item el-menu-item index/system/role角色管理/el-menu-item /el-sub-menu /el-menu /el-aside el-container el-header div classheader-left el-icon classcollapse-btn clickisCollapse !isCollapse Fold / /el-icon /div /el-header el-main router-view / /el-main /el-container /el-container /template注意几个要点el-menu配router属性后index就是路由路径点击菜单项直接跳转省去select手动跳转的代码折叠菜单时el-menu会自动收起为图标模式但需要给el-menu-item里的span外面包一层模板否则折叠状态下文字会挡住图标el-header默认高度 60px可以覆盖el-main默认有 padding根据需求调整。4.2 用 SCSS 变量覆盖主题色Element Plus 的定制主题有两条路SCSS 变量覆盖和 CSS 变量覆盖。SCSS 变量方式在导入源码样式的前提下使用// styles/element/index.scss forward element-plus/theme-chalk/src/common/var.scss with ( $colors: ( primary: ( base: #6366f1 ) ) );然后在 Vite 里配置css.preprocessorOptions.scss.additionalData或者在引入组件样式时替换为自己的 SCSS 入口。这个方案配置链路较长而且每次升级组件库都要重新编译验证。更推荐的方式是直接用 CSS 变量覆盖。Element Plus 的组件样式大量使用 CSS 变量开发环境按 F12 看样式的computed面板就能看到很多--el-color-primary这样的变量。在入口样式表覆盖即可:root { --el-color-primary: #6366f1; --el-color-primary-light-3: #8b8ef5; --el-color-primary-light-5: #a5a7f8; --el-color-primary-light-7: #c0c1fa; --el-color-primary-light-8: #cdcefb; --el-color-primary-light-9: #e7e7fd; --el-color-primary-dark-2: #4f52c1; }只需要把--el-color-primary换掉整个组件库的按钮、链接、选中色、加载色都会跟着变快捷干净。实际项目中我基本都是靠这套 CSS 变量方案做主题定制不太建议轻易去改 SCSS。4.3 暗黑模式切换Element Plus 从 2.2.0 版本开始原生支持暗黑模式实现方式非常轻量。先在入口文件引入暗黑模式样式import element-plus/theme-chalk/dark/css-vars.css然后在需要切换暗黑模式的地方给html标签加上dark类import { ref, watchEffect } from vue const isDark ref(false) watchEffect(() { const htmlEl document.documentElement if (isDark.value) { htmlEl.classList.add(dark) } else { htmlEl.classList.remove(dark) } })切换时把isDark存到 localStorage 或 Pinia 里刷新后保持用户偏好。Element Plus 暗黑模式本质上是使用了html.dark这个类选择器去覆盖 CSS 变量所以项目里如果自己写了深色背景也要在自己的样式中配合html.dark做适配。4.4 样式覆盖不生效的排查思路后台系统里改第三方组件样式是常态但经常遇到改了没反应。按这个顺序排查基本能解决问题检查选择器优先级。Element Plus 的样式优先级不低你写的样式是不是被它的类名压住了检查 scoped 隔离。组件内写了scoped不加:deep()根本穿透不到子组件检查 CSS 文件加载顺序。如果自定义主题文件在 Element Plus 样式之前加载同名选择器会被后加载的样式覆盖检查浏览器缓存。开发环境偶尔有样式缓存导致改了看不到效果强制刷新一次试试。5. 中文环境适配国际化与日期组件的坑Element Plus 默认组件文案是英文直接用在中文后台里特别突兀——分页器显示 “Total 100 / 10 pages”日期选择器显示英文月份上传组件按钮写 “Upload”。这些文案要一次性全部切换成中文同时还要照顾日期组件的格式化显示格式。5.1 用 el-config-provider 包裹根组件Element Plus 官方推荐用el-config-provider组件做全局语言配置。在App.vue的根节点外面包一层!-- App.vue -- template el-config-provider :localelocale router-view / /el-config-provider /template script setup langts import { ref } from vue import zhCn from element-plus/es/locale/lang/zh-cn import en from element-plus/es/locale/lang/en const locale ref(zhCn) /scriptzhCn这个对象包含了几乎所有 Element Plus 内置组件的文案分页器、日期选择器、表格的过滤、弹窗的确定和取消按钮、上传组件的提示文字等等。一包解决。5.2 动态切换语言如果系统需要支持中英文切换把locale变成响应式即可。配一个全局状态// stores/app.ts import { defineStore } from pinia import { ref } from vue import zhCn from element-plus/es/locale/lang/zh-cn import en from element-plus/es/locale/lang/en export const useAppStore defineStore(app, () { const locale ref(zhCn) const localeKey ref(zh-cn) const setLocale (key: zh-cn | en) { localeKey.value key locale.value key zh-cn ? zhCn : en } return { locale, localeKey, setLocale } })然后App.vue里从 store 取locale。切换时const appStore useAppStore() appStore.setLocale(en)这样 Element Plus 组件文案和业务代码共享同一套语言状态切换即时生效。5.3 日期组件的格式化日期相关组件有两个容易混淆的属性format控制输入框里显示的格式比如YYYY-MM-DDvalue-format控制绑定值的格式决定最终提交给后端的数据长什么样。后端接口一般接受字符串日期不想让组件给你返回 Date 对象就给value-format指定格式el-date-picker v-modelformData.date typedate placeholder选择日期 formatYYYY-MM-DD value-formatYYYY-MM-DD /注意Element Plus 的日期格式化用的是 dayjs 语法YYYY-MM-DD HH:mm:ss区别于原生 JS 的yyyy-MM-dd。很多从老项目迁移过来的人容易在这踩坑格式化出来全是 undefined。5.4 语言包的合并与自定义zhCn里不包含业务自身的文案如果某些组件文案不符合团队习惯可以对语言包做浅合并import zhCn from element-plus/es/locale/lang/zh-cn const customLocale { ...zhCn, el: { ...zhCn.el, table: { ...zhCn.el.table, emptyText: 暂无数据请联系管理员配置 } } }然后把customLocale传给el-config-provider。这个方案适合不想改源码又需要定制文案的场景值得收藏。6. 高频报错与优化跑通之后真正要花时间的部分接入 Element Plus 最耗时间的往往不是组件怎么用而是项目跑到一半冒出来的各种报错和体积问题。这里我把遇到的几个高频问题逐一拆解给出排查链路和解决方案。6.1 TS 项目里的类型报错如果你的项目是vue-ts模板创建接入 Element Plus 后最常见的报错是Cannot find module element-plus or its corresponding type declarations.或者组件模板里el-button的属性类型推断失败。排查链路是这样先确认package.json里确实安装了element-plus。安装成功后检查src/vite-env.d.ts里有没有官方推荐的引用/// reference typesvite/client /组件类型声明这一块unplugin-vue-components会在项目根目录自动生成components.d.ts正常情况下组件模板里的类型自动就补上了。如果还是报错检查tsconfig.json的include是否覆盖了src目录和自动生成的components.d.ts文件。若依 Vue3 版本里出现过的高频报错是全局属性类型问题。在main.ts里给app.config.globalProperties挂了$message这样的属性然后组件里this.$message去调用TS 直接报错。解决办法是在src/types下写一个全局类型声明// src/types/global.d.ts import type { Message } from element-plus declare module vue { interface ComponentCustomProperties { $message: typeof Message } } export {}这属于 Vue3 的ComponentCustomProperties接口扩展很多老后台项目迁移时都会遇到记住这个声明文件的位置就行。6.2 pxtorem 对 ECharts 没效果的排查这个问题在热搜词里出现了多次。项目里接了pxtorem做移动端适配结果 ECharts 图表的字体和尺寸没有跟着 rem 缩放。原因很直接ECharts 初始化后是在canvas上绘制的canvas 里的文字和图形是绘制出来的位图或者说矢量图形不是 DOM 节点pxtorem这类工具只能处理 CSS 样式值对 canvas 内部绘制逻辑没有作用。常规解法是让图表容器参与 rem 适配容器宽度变化后触发 ECharts 的resize()同时设置一个基准字号通过脚本动态计算图表内部 fontSizeimport * as echarts from echarts const chart echarts.init(container.value) const baseFontSize document.documentElement.clientWidth / 10 // 假设 10rem 等于屏宽 chart.setOption({ title: { text: 销售趋势, textStyle: { fontSize: baseFontSize * 0.16 } } }) window.addEventListener(resize, () { chart.resize() })这是 canvas 图表和 CSS 单位体系之间的固有割裂理解了原理之后就能对症下药。6.3 按需引入后 ElMessage 样式失效unplugin-auto-import导入ElMessage后调用时弹出的是裸奔的文本没有背景色和边框。原因是ElMessage的 API 被自动导入了但对应的样式没有被自动导入。排查链路检查vite.config.ts里AutoImport和Components的 resolver 是否正确传递了ElementPlusResolver()。这个 resolver 会同时处理 JS 和 CSS。如果两个插件都配了再看main.ts里有没有手动引入element-plus/dist/index.css——如果已经手动引入了全量样式按需引入组件的意义就打折扣了两者混用很容易出问题。标准做法是用 unplugin 插件配置自动引入之后main.ts里不要手动引全量 CSS删除import element-plus/dist/index.css这行。如果确实有部分组件样式不对确认 resolver 是否正确处理对应组件的样式导入。6.4 打包体积优化自动按需引入只是第一步后台管理系统组件种类多打包体积依然能过 MB 级别。几个可落地的优化手段在vite.config.ts里开启构建分析先看看到底哪些包体积最大npm install -D rollup-plugin-visualizer配置分析插件后构建完会自动打开体积报告能清楚看到echarts、element-plus、vue各占多少。常用的优化手段路由级代码分割const UserList () import(/views/UserList.vue)按需加载页面模块ECharts 按模块引入不用全量import * as echarts from echarts改成import { LineChart, BarChart } from echarts/charts构建 target 调低并开 gzipVite 默认 target 是modules兼容现代浏览器可以保持如果要兼容旧版浏览器需要降 target代价是产出代码体积变大。gzip/brotli 压缩用vite-plugin-compression。大项目一次打包任务ECharts 按需引入加路由懒加载体积能减少 30% 左右再接上 gzip上线传输体积还能再压一半效果非常明显。6.5 浏览器兼容与版本锁定Element Plus 官方声明不支持 IE11如果你的系统还要在 IE 里跑趁早换方案别浪费时间配置 polyfill。版本锁定也很重要。element-plus的 minor 版本之间偶尔会有破坏性更新package.json里建议锁版本{ dependencies: { element-plus: 2.7.0 } }选一个稳定版本团队内部统一不要经常自动升级到最新版本省得出现样式、API 说变就变的情况。最后再说两句实在话接入 Element Plus 这件事难的不是安装和引入而是后面那些组合拳页面布局、表单校验、主题定制、页面性能。我自己的习惯是先完整引入把原型跑通确认交互和功能没问题之后再切换到自动按需引入并做体积优化——因为开发阶段全量引入确实省心到了构建阶段再按需引入也不迟。项目跑起来之后再花点时间把el-config-provider的中文配置补上顺手把--el-color-primary换成品牌色。这些都是小改动但能明显提升系统的完成度。如果你是从 Vue2 的 element-ui 迁移过来最大的心理障碍不是 API 变了多少而是“旧习惯得改”。多翻官方文档多看实际渲染出来的 DOM 结构写代码之前先想清楚组件本质上是“配置驱动的渲染逻辑”很多问题就不会卡住太久。
返回列表