vite 动态导入 element

使用element-plus + vite按需引入的方式,当首次启动vite服务时会对style进行依赖预构建,并且在切换不同路由时如果其他模块有使用到新的组件,页面会卡住直至dependencies optimized完成:

dependencies optimized

解决方案

开发时全量导入element-plus组件,打包时按需导入。

开发环境完整引入

针对开发环境,我们希望完整引入 element-plus 的所有组件。在 vite.config.js 中通过添加一个自定义插件来实现这个功能:

import process from 'node:process' import { defineConfig } from 'vite' import ElementPlus from 'unplugin-element-plus/vite' export default defineConfig({ // ... plugins: [ // ... process.env.NODE_ENV === 'development' ? { name: 'vite:element-plus-auto-import-in-dev', transform(code, id) { if (/src\/main.ts$/.test(id)) { return { code: ` import ElementPlus from 'element-plus' import 'element-plus/theme-chalk/src/index.scss' ${code.split('const app = createApp(App)').join('const app = createApp(App);app.use(ElementPlus);')}; `, map: null, } } }, } : ElementPlus({ useSource: true }), ], })
  • transform: 用于修改代码的钩子函数,返回值是一个对象,包含修改后的代码和 sourcemap。
  • process.env.NODE_ENV: 用于判断当前是否是开发环境。
  • src/main.ts: 用于匹配入口文件。
  • code: 用于修改入口文件的代码,将 ElementPlus 注入到 app 中。
  • map: 用于 sourcemap,这里不需要 sourcemap,所以设置为 null。
  • split: 此处是为了避免在APP.vue中使用了el-config-provider,导致页面比ElementPlus加载快,因此需要在mount之前引入ElementPlus

引入css的时候最好是用以上的方式,如果使用import 'element-plus/dist/index.css'的方式,可能会存在自定义样式覆盖不到的问题。

tsconfig.json中添加 element-plus 的类型声明:

{ "compilerOptions": { "types": [ // ... "element-plus/global" ] } }

打包按需引入

在生产环境,我们希望按需引入 element-plus 的组件,这样可以减少打包后的体积,通过配置unplugin-vue-components插件来实现这个功能:

import process from 'node:process' import { defineConfig } from 'vite' import Components from 'unplugin-vue-components/vite' import { ElementPlusResolver } from 'unplugin-vue-components/resolvers' export default defineConfig({ // ... plugins: [ // ... Components({ dts: 'src/components.d.ts', include: [/\.vue$/, /\.vue\?vue/], exclude: ['src/components/**/components/**/*'], resolvers: process.env.NODE_ENV === 'production' ? ElementPlusResolver({ importStyle: 'sass' }) : undefined, }), ], })

unplugin-vue-components的具体配置项可以查看GithubElementPlusResolver具体配置项可以查看官方文档或者Github

还需注意

因为这个方案是在开发环境进行全局引入,打包构建后还是保留按需引入。所以在使用 message 这类函数组件时,需要使用import { ElMessage } from 'element-plus'这种手动导入的方式。如果嫌麻烦则可以使用 unplugin-auto-import ,并且在 optimizeDeps 里对这类反馈组件的样式进行预构建:

import path from 'node:path' import { defineConfig } from 'vite' import AutoImport from 'unplugin-auto-import/vite' export default defineConfig({ // ... optimizeDeps: { include: [ '@element-plus/icons-vue', 'element-plus/es', 'element-plus/es/components/base/style/index', 'element-plus/es/components/message/style/index', 'element-plus/es/components/message-box/style/index', 'element-plus/es/components/notification/style/index', ], }, plugins: [ // ... AutoImport({ imports: [ 'vue', 'pinia', '@vueuse/core', VueRouterAutoImports, { // add any other imports you were relying on 'vue-router/auto': ['useLink'], }, ], resolvers: [ElementPlusResolver({ importStyle: 'sass' })], dts: 'src/auto-imports.d.ts', dirs: [ 'src/composables', 'src/events', 'src/stores', 'src/utils', ], vueTemplate: true, }), ], })