vue3+element-plus按需自动导入-正确姿势

1.场景

在开发的时候我们常常需要按需导入第三方库,利用工具的tree shake实现打包体积最小化。

往往需要手工在每个页面单独导入和费时。这里可以利用工具如unplugin-auto-import unplugin-vue-components等插件工具简化繁琐的导入。

2.vue

全量导入

在实现按需之前,我们看看粗暴的全量导入,这样才有比较好的对比

创建项目

sh 复制代码
npm create vite@latest
npm i -D element-plus
npm i -D vite-bundle-analyzer # 用于输出打包大小做对比

配置vite (这里我们先禁用代码混淆压缩)

js 复制代码
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { analyzer } from 'vite-bundle-analyzer'

// https://vite.dev/config/
export default defineConfig({
  plugins: [ 
    vue(), 
    analyzer()
  ] ,
  build: {
    minify: false, // 禁用代码压缩混淆,方便查看
  }
})

修改src/components/HelloWorld.vue

这里我们只引入的ref

js 复制代码
<script setup lang="ts">
import { ref } from 'vue'

defineProps<{ msg: string }>()

const count = ref(0)
</script>

<template>
  <h1>{{ msg }}</h1>

  <div class="card">
    <button type="button" @click="count++">count is {{ count }}</button> 
  </div> 
</template>

运行输出报告

js 复制代码
npm run build

这里可以看到默认打包js的大小,这里大小是只使用了ref的大小 查看输出源码只有ref的定义

验证vite tree shake

我们再次修改src/components/HelloWorld.vue

js 复制代码
<script setup lang="ts">
import { ref } from 'vue'
import * as Vue from 'vue' 
const allkey = Object.keys(Vue)
defineProps<{ msg: string }>()

const count = ref(0)
</script>

<template>
  <h1>{{ msg }}</h1>

  <div class="card">
    <button type="button" @click="count++">count is {{ count }}</button> 
    {{allkey}}
  </div> 
</template>

我们通过引入这个vue 并遍历打印到页面,这样vue的库就被引入。

再次build,这次200多k ,证明tree shake有效果

并且源码输出了很多其他的api代码 不只是ref了

按需自动引入vue

调整代码 注释掉全部引入vue的代码

js 复制代码
<script setup lang="ts">
import { ref } from 'vue'
// import * as Vue from 'vue' 
// const allkey = Object.keys(Vue)
defineProps<{ msg: string }>()

const count = ref(0)
</script>

<template>
  <h1>{{ msg }}</h1>

  <div class="card">
    <button type="button" @click="count++">count is {{ count }}</button> 
    <!-- {{allkey}} -->
  </div> 
</template>

vite.config.ts 把混淆压缩打开,注释minify

js 复制代码
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { analyzer } from 'vite-bundle-analyzer'

// https://vite.dev/config/
export default defineConfig({
  plugins: [ 
    vue(), 
    analyzer()
  ] ,
  build: {
    // minify: false, // 禁用代码压缩混淆
  }
})

再次构建

正常使用ref 打包tree shake的压缩的大小59kb

现在我们要实现自动引入

为了偷懒少写import,我们使用unplugin-auto-import

js 复制代码
npm i unplugin-auto-import -D

配置vite

js 复制代码
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { analyzer } from 'vite-bundle-analyzer'
import AutoImport from "unplugin-auto-import/vite";

// https://vite.dev/config/
export default defineConfig({
  plugins: [ 
    vue(), 
     AutoImport({
      imports: [
          'vue'
        ], 
     }),
    analyzer()
  ] ,
  build: {
    // minify: false, // 禁用代码压缩混淆
  }
})

再次构建,在代码根目录会多一个auto-imports.d.ts 文件,

js 复制代码
/* eslint-disable */
/* prettier-ignore */
// @ts-nocheck
// noinspection JSUnusedGlobalSymbols
// Generated by unplugin-auto-import
// biome-ignore lint: disable
export {}
declare global {
  const EffectScope: typeof import('vue').EffectScope
  const computed: typeof import('vue').computed
  const createApp: typeof import('vue').createApp
  const customRef: typeof import('vue').customRef
  const defineAsyncComponent: typeof import('vue').defineAsyncComponent
  const defineComponent: typeof import('vue').defineComponent
  const effectScope: typeof import('vue').effectScope
  const getCurrentInstance: typeof import('vue').getCurrentInstance
  const getCurrentScope: typeof import('vue').getCurrentScope
  const getCurrentWatcher: typeof import('vue').getCurrentWatcher
  const h: typeof import('vue').h
  const inject: typeof import('vue').inject
  const isProxy: typeof import('vue').isProxy
  const isReactive: typeof import('vue').isReactive
  const isReadonly: typeof import('vue').isReadonly
  const isRef: typeof import('vue').isRef
  const isShallow: typeof import('vue').isShallow
  const markRaw: typeof import('vue').markRaw
  const nextTick: typeof import('vue').nextTick
  const onActivated: typeof import('vue').onActivated
  const onBeforeMount: typeof import('vue').onBeforeMount
  const onBeforeUnmount: typeof import('vue').onBeforeUnmount
  const onBeforeUpdate: typeof import('vue').onBeforeUpdate
  const onDeactivated: typeof import('vue').onDeactivated
  const onErrorCaptured: typeof import('vue').onErrorCaptured
  const onMounted: typeof import('vue').onMounted
  const onRenderTracked: typeof import('vue').onRenderTracked
  const onRenderTriggered: typeof import('vue').onRenderTriggered
  const onScopeDispose: typeof import('vue').onScopeDispose
  const onServerPrefetch: typeof import('vue').onServerPrefetch
  const onUnmounted: typeof import('vue').onUnmounted
  const onUpdated: typeof import('vue').onUpdated
  const onWatcherCleanup: typeof import('vue').onWatcherCleanup
  const provide: typeof import('vue').provide
  const reactive: typeof import('vue').reactive
  const readonly: typeof import('vue').readonly
  const ref: typeof import('vue').ref
  const resolveComponent: typeof import('vue').resolveComponent
  const shallowReactive: typeof import('vue').shallowReactive
  const shallowReadonly: typeof import('vue').shallowReadonly
  const shallowRef: typeof import('vue').shallowRef
  const toRaw: typeof import('vue').toRaw
  const toRef: typeof import('vue').toRef
  const toRefs: typeof import('vue').toRefs
  const toValue: typeof import('vue').toValue
  const triggerRef: typeof import('vue').triggerRef
  const unref: typeof import('vue').unref
  const useAttrs: typeof import('vue').useAttrs
  const useCssModule: typeof import('vue').useCssModule
  const useCssVars: typeof import('vue').useCssVars
  const useId: typeof import('vue').useId
  const useModel: typeof import('vue').useModel
  const useSlots: typeof import('vue').useSlots
  const useTemplateRef: typeof import('vue').useTemplateRef
  const watch: typeof import('vue').watch
  const watchEffect: typeof import('vue').watchEffect
  const watchPostEffect: typeof import('vue').watchPostEffect
  const watchSyncEffect: typeof import('vue').watchSyncEffect
}
// for type re-export
declare global {
  // @ts-ignore
  export type { Component, Slot, Slots, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, ShallowRef, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue'
  import('vue')
}

这里会根据所有代码上下文,在我们实际代码自动加入import,并且自动导出了 auto-imports.d.ts需要的全局变量类型,这样我们在vscode就知道这个全局定义不会报错。

正常在没有导入ref的时候,使用ref,ts会报错找不到

回到 HelloWorld.vue 文件,可以看到错误消失了。

再次构建正常,大小不变,但是已经可以不再繁琐的导入ref了

大小依然是59kb nice

3.element-plus

同理我们在element-plus操作一遍。

全局导入

sh 复制代码
npm i element-plus -D

main.ts 全量引入

js 复制代码
import { createApp } from 'vue'
// import './style.css'
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')

src/components/HelloWorld.vue 加入el-button 按钮

js 复制代码
<script setup lang="ts">
// import { ref } from 'vue'
// import * as Vue from 'vue' 
// const allkey = Object.keys(Vue)

import { ElMessage } from 'element-plus'

ElMessage.error(`xxxx`)
defineProps<{ msg: string }>()

const count = ref(0)
</script>

<template>
  <h1>{{ msg }}</h1>

  <div class="card">
    <button type="button" @click="count++">count is {{ count }}</button> 
    <el-button type="primary">Element Plus Button</el-button>
    <!-- {{allkey}} -->
  </div> 
</template>

我们加入el-button组件,和 ElMessage提示

vite.config.ts 把混淆压缩打开,注释minify

js 复制代码
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { analyzer } from 'vite-bundle-analyzer'
import AutoImport from "unplugin-auto-import/vite";

// https://vite.dev/config/
export default defineConfig({
  plugins: [ 
    vue(), 
     AutoImport({
      imports: [
          'vue', 
        ],
     }),
    analyzer()
  ] ,
  build: {
   // minify: false, // 禁用代码压缩混淆
  }
})

由于是全量,打包后大小1.24m(注意这里是包含的vue和element-plus)

手动导入

在还没自动导入前,我们一般手动都是 一个个组件注册并导入,同时导入对应的css文件

新建 src/plugins/element-plus.ts

js 复制代码
import { ElButton, ElInput, ElMessage, ElTable, ElTableColumn,ElMessageBox ,ElForm, ElUpload } from 'element-plus'

// 组件列表
const components = [ElButton, ElMessage ]

// 插件安装方法
const install = (app :any) => {
  components.forEach((component) => {
    app.component(component.name, component)
  })
 
}

export default {
  install,
  ElMessage
}

通过 components 数组定义自己需要引入的组件

main.ts

ts 复制代码
import { createApp } from 'vue'
// import './style.css'
// import ElementPlus from 'element-plus' 
import elementPlusPlugins from './plugins/element-plus'
// import 'element-plus/dist/index.css'

import 'element-plus/es/components/base/style/css'// 基础样式
// import 'element-plus/es/components/badge/style/css'
import 'element-plus/es/components/button/style/css'// 对应按钮样式
import 'element-plus/es/components/message/style/css'// 对应消息样式

import App from './App.vue' 

const app = createApp(App)

// app.use(ElementPlus)
app.use(elementPlusPlugins)
app.mount('#app')

重新构建,总大小只有143k

上面的导入css 其实有对应的自动import工具叫unplugin-element-plus/vite

sh 复制代码
npm i unplugin-element-plus sass-embedded -D

修改 vite

js 复制代码
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { analyzer } from 'vite-bundle-analyzer'
import AutoImport from "unplugin-auto-import/vite";
import ElementPlusStyle from 'unplugin-element-plus/vite'

// https://vite.dev/config/
export default defineConfig({
  plugins: [ 
    vue(), 
     AutoImport({
      imports: [
          'vue', 
        ],
     }),
    ElementPlusStyle({
      // 选项
      useSource: true
    }),
    analyzer()
  ] ,
  build: {
    // minify: false, // 禁用代码压缩混淆
  }
})

注释所有 css

ts 复制代码
import { createApp } from 'vue'
// import './style.css'
// import ElementPlus from 'element-plus' 
import elementPlusPlugins from './plugins/element-plus'
// import 'element-plus/dist/index.css'

// import 'element-plus/es/components/base/style/css'
// import 'element-plus/es/components/badge/style/css'
// import 'element-plus/es/components/button/style/css'
// import 'element-plus/es/components/message/style/css'

import App from './App.vue' 

const app = createApp(App)

// app.use(ElementPlus)
app.use(elementPlusPlugins)
app.mount('#app')

重新运行 ,css正常加载

实际大小比上面大一点点

自动导入

要实现组件自动安装可以使用 unplugin-vue-components

sh 复制代码
npm i unplugin-vue-components -D

vite

js 复制代码
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { analyzer } from 'vite-bundle-analyzer'
import AutoImport from "unplugin-auto-import/vite";
import ElementPlusStyle from 'unplugin-element-plus/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'

// https://vite.dev/config/
export default defineConfig({
  plugins: [ 
    vue(), 
     AutoImport({
      imports: [
          'vue', 
        ],
     resolvers: [ElementPlusResolver()], // 这里用于解决
     }), 
   ,
     Components({
      resolvers: [ElementPlusResolver()],
    }),

    ElementPlusStyle({
      // 选项
      useSource: true
    }),
    analyzer()
  ] ,
  build: {
    // minify: false, // 禁用代码压缩混淆
  }
})

注意:

  • unplugin-auto-import : 解决的事js ts的import 问题
  • unplugin-vue-components: 解决组件引入和样式问题
  • unplugin-vue-components/resolvers 是针对不同ui自带的导入出

重新构建,这时候,除了之前的auto-imports.d.ts ,

还多了个 components.d.ts,他用于自动注册所有组件

auto-imports.d.ts

js 复制代码
/* eslint-disable */
/* prettier-ignore */
// @ts-nocheck
// noinspection JSUnusedGlobalSymbols
// Generated by unplugin-auto-import
// biome-ignore lint: disable
export {}
declare global {
  const EffectScope: typeof import('vue').EffectScope
  const ElMessage: typeof import('element-plus/es').ElMessage // 这里多了 HelloWorld.vue里面使用的ElMessage ,这样ts就不会报错
  const ElMessageBox: typeof import('element-plus/es').ElMessageBox
  const computed: typeof import('vue').computed
  const createApp: typeof import('vue').createApp
  const customRef: typeof import('vue').customRef
  const defineAsyncComponent: typeof import('vue').defineAsyncComponent
  const defineComponent: typeof import('vue').defineComponent
  const effectScope: typeof import('vue').effectScope
  const getCurrentInstance: typeof import('vue').getCurrentInstance
  const getCurrentScope: typeof import('vue').getCurrentScope
  const getCurrentWatcher: typeof import('vue').getCurrentWatcher
  const h: typeof import('vue').h
  const inject: typeof import('vue').inject
  const isProxy: typeof import('vue').isProxy
  const isReactive: typeof import('vue').isReactive
  const isReadonly: typeof import('vue').isReadonly
  const isRef: typeof import('vue').isRef
  const isShallow: typeof import('vue').isShallow
  const markRaw: typeof import('vue').markRaw
  const nextTick: typeof import('vue').nextTick
  const onActivated: typeof import('vue').onActivated
  const onBeforeMount: typeof import('vue').onBeforeMount
  const onBeforeUnmount: typeof import('vue').onBeforeUnmount
  const onBeforeUpdate: typeof import('vue').onBeforeUpdate
  const onDeactivated: typeof import('vue').onDeactivated
  const onErrorCaptured: typeof import('vue').onErrorCaptured
  const onMounted: typeof import('vue').onMounted
  const onRenderTracked: typeof import('vue').onRenderTracked
  const onRenderTriggered: typeof import('vue').onRenderTriggered
  const onScopeDispose: typeof import('vue').onScopeDispose
  const onServerPrefetch: typeof import('vue').onServerPrefetch
  const onUnmounted: typeof import('vue').onUnmounted
  const onUpdated: typeof import('vue').onUpdated
  const onWatcherCleanup: typeof import('vue').onWatcherCleanup
  const provide: typeof import('vue').provide
  const reactive: typeof import('vue').reactive
  const readonly: typeof import('vue').readonly
  const ref: typeof import('vue').ref
  const resolveComponent: typeof import('vue').resolveComponent
  const shallowReactive: typeof import('vue').shallowReactive
  const shallowReadonly: typeof import('vue').shallowReadonly
  const shallowRef: typeof import('vue').shallowRef
  const toRaw: typeof import('vue').toRaw
  const toRef: typeof import('vue').toRef
  const toRefs: typeof import('vue').toRefs
  const toValue: typeof import('vue').toValue
  const triggerRef: typeof import('vue').triggerRef
  const unref: typeof import('vue').unref
  const useAttrs: typeof import('vue').useAttrs
  const useCssModule: typeof import('vue').useCssModule
  const useCssVars: typeof import('vue').useCssVars
  const useId: typeof import('vue').useId
  const useModel: typeof import('vue').useModel
  const useSlots: typeof import('vue').useSlots
  const useTemplateRef: typeof import('vue').useTemplateRef
  const watch: typeof import('vue').watch
  const watchEffect: typeof import('vue').watchEffect
  const watchPostEffect: typeof import('vue').watchPostEffect
  const watchSyncEffect: typeof import('vue').watchSyncEffect
}
// for type re-export
declare global {
  // @ts-ignore
  export type { Component, Slot, Slots, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, ShallowRef, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue'
  import('vue')
}

components.d.ts

js 复制代码
/* eslint-disable */
// @ts-nocheck
// biome-ignore lint: disable
// oxlint-disable
// ------
// Generated by unplugin-vue-components
// Read more: https://github.com/vuejs/core/pull/3399

export {}

/* prettier-ignore */
declare module 'vue' {
  export interface GlobalComponents {
    ElButton: typeof import('element-plus/es')['ElButton']
    HelloWorld: typeof import('./src/components/HelloWorld.vue')['default']
  }
}

重新打包依然是 143kb

4.实际项目应用

在实际开发中我们还有自己定义的方法,如果也想偷懒也可以借用auto-import

添加工具类 src/util.ts

js 复制代码
export function sum(a: number, b: number) {
  return a + b;
}   

HelloWorld.vue 引入sum并输出

js 复制代码
<script setup lang="ts">
// import { ref } from 'vue'
// import * as Vue from 'vue' 
// const allkey = Object.keys(Vue)

// import { ElMessage } from 'element-plus'
import {sum} from '../util'

ElMessage.error(`xxxx`)
defineProps<{ msg: string }>()

const count = ref(0)
</script>

<template>
  <h1>{{ msg }}</h1>

  <div class="card">
    <button type="button" @click="count++">count is {{ count }}</button> 
    <el-button type="primary">Element Plus Button</el-button>
    <p>{{sum(1,2)}}</p> 
    <!-- {{allkey}} -->
  </div> 
</template>

修改vite

js 复制代码
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { analyzer } from 'vite-bundle-analyzer'
import AutoImport from "unplugin-auto-import/vite";
// import ElementPlusStyle from 'unplugin-element-plus/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'

// https://vite.dev/config/
export default defineConfig({
  plugins: [ 
    vue(), 
     AutoImport({
      imports: [
          'vue', 
           {
            'src/util': ['sum'],  // 这里加入引用
          }
        ],
     resolvers: [ElementPlusResolver()], // 这里用于解决
     }), 
   ,
     Components({
      resolvers: [ElementPlusResolver()],
    }),

    // ElementPlusStyle({
    //   // 选项
    //   useSource: true
    // }),
    analyzer()
  ] ,
  build: {
    // minify: false, // 禁用代码压缩混淆
  }
})

再次构建 auto-imports.d.ts 多出了这个sum方法

这样在HelloWorld.vue 就可以省略引入,

好处

在实际应用中不仅节省的导入的时间,尤其大量页面都是类似的业务逻辑,臃肿的代码就不复纯在在,你只需要关心最差异化的处理,如api的调用差异

上面代码只剩下 关键的api处理

缺点

由于是全局引入,有可能有命名冲突的风险,所以命名要规范。

5.参考代码

github.com/mjsong07/vu...

相关推荐
泉城老铁5 小时前
vue实现前端excel的导出
前端·vue.js
用户51681661458415 小时前
Lottie动画在前端web、vue、react中使用详解
前端·vue.js
咖啡の猫6 小时前
Vue收集表单数据
前端·javascript·vue.js
知识分享小能手6 小时前
uni-app 入门学习教程,从入门到精通, uni-app常用API的详细语法知识点(上)(5)
前端·javascript·vue.js·学习·微信小程序·小程序·uni-app
_志哥_6 小时前
多行文本超出,中间显示省略号的终极方法(适配多语言)
前端·javascript·vue.js
我是日安7 小时前
从零到一打造 Vue3 响应式系统 Day 29 - readonly:数据保护实现
前端·javascript·vue.js
૮・ﻌ・7 小时前
Vue2(一):创建实例、插值表达式、Vue响应式特性、Vue指令、指令修饰符、计算属性
前端·javascript·vue.js
半生过往8 小时前
2025 前端动效实战指南:Vue Bits & React Bits 深度拆解(功能 / 复用 / 高频问题处理)
前端·vue.js·react.js
BumBle8 小时前
使用 SortableJS 实现vue3 + Element Plus 表格拖拽排序
前端·vue.js·element