使用 vite 搭建组件库、全量打包组件库的实践过程
目录
1. 根据 vite 文档,新建项目,安装 elementplus、vue-router
4. 在 components 目录下,新建 index.ts
5. 在 main.ts 中使用刚刚导出的全量组件
6. 在 views 目录下使用组件
7. 在 src 目录下新建 packages 目录,并添加内容
8. 新建 command/build.js 实现全量打包命令配置
9. 修改 package.json ,执行组件库全量打包命令
10. 在项目中使用上面打包的组件库
1. 根据 vite 文档,新建项目,安装 elementplus、vue-router
截止 2022-03-24,最新版的 element-plus,会自动安装 elementplus 的 icons 模块
yarn create vite my-vue-app --template vue
yarn add element-plus
yarn add vue-router
2. 在 components/menu/src 目录下,新建 index.vue
作用:封装菜单组件(使用 elementplus 对 导航组件 进行二次封装)
{{ item[name] }} {{ item[name] }} {{ item1[name] }} // 固定层数的导航菜单(最多二级)import { PropType } from 'vue'import { toLine } from '../../../utils'let props = defineProps({ // 菜单数据 data: { // 因为用户可能会自定义键名,所以此处不给他指定具体数据类型 MenuItem type: Array as PropType, required: true, }, // 默认选中的菜单项 defaultActive: { type: String, default: '', }, // 是否是路由模式 router: { type: Boolean, default: false, }, // ======== 允许用户自定义键名 ======== // 菜单标题的键名 name: { type: String, default: 'name', }, // 菜单标识的键名 index: { type: String, default: 'index', }, // 菜单图标的键名 icon: { type: String, default: 'icon', }, // 菜单子菜单的键名 children: { type: String, default: 'children', },})svg { margin-right: 4px; width: 1em; height: 1em;}.el-menu-vertical-demo:not(.el-menu--collapse) { width: 200px;}
3. 在 components/menu 目录下,新建 index.ts
作用:注册全局组件,实现按需引入(让组件可以通过 app.use() 的形式使用)
注意:引入组件时,一定要加上 .vue 后缀;如果不加,会导致打包后生成的 .es.js 无法在项目中使用
import { App } from 'vue'import menu from './src/index.vue'// 让组件可以通过 app.use() 的形式使用export default { install(app: App) { app.component('l-menu', menu) }}
4. 在 components 目录下,新建 index.ts
作用:注册全局组件,实现全局引入(让组件可以通过 app.use() 的形式使用);最终在 views 下使用的组件,其实是这个 index.ts 中注册的组件;
注意:这里引入的是 组件目录下的 index.ts 文件,不是 index.vue 组件
import { App } from 'vue'import menu from './menu'// 最终在 views 下使用的组件,其实是这个 index.ts 中注册的组件const components = [ menu,]export default { /** * 让组件可以通过 app.use() 的形式使用 * @param app */ install(app: App) { components.map(item => { app.use(item) }) }}
5. 在 main.ts 中使用刚刚导出的全量组件
import { createApp } from 'vue'import App from './App.vue'// 引入 路由import router from './router/index'// 引入 elementplusimport ElementPlus from 'element-plus'import 'element-plus/dist/index.css'// 引入 element-plus Iconimport * as Icons from '@element-plus/icons-vue'import { toLine } from './utils/index'import lBusinessComponents from './components/index'// import lBusinessComponents from '../lib/l-business-components.es.js'// import '../lib/style.css'const app = createApp(App)// 全局注册图标 牺牲一点性能for (let i in Icons) { /** * console.log((Icons as any)[i]) * 默认情况下,会打印出 Object,使用驼峰命名,如: "Switch" * 希望转换成 el-icon-xxx 的形式,就需要用新的方式全局注册组件 */ // 注册全部 elementplus Icon 组件(使用方法:el-icon-xxx) app.component(`el-icon-${toLine(i)}`, (Icons as any)[i])}app.use(router)app.use(ElementPlus)app.use(lBusinessComponents)app.mount('#app')
6. 在 views 目录下使用组件
如果组件可以正常的渲染出来,则证明组件已经实现了全局注册,接下来可以进行打包操作啦
7. 在 src 目录下新建 packages 目录,并添加内容
下面每一步都很重要哦 ❗ ❗ ❗
把 components 目录下的所有内容,复制到 packages 目录下;
同时还要复制 hooks、styles、utils 等文件夹 到 packages 目录下,因为组件用到了这些内容,他们需要一起被打包;
修改 packages/index.ts,引入需要的样式文件、工具文件(不是组件特有的,是全局存放的,这一步非常容易忘);
我在下面的文件中,引入了两个组件,一个是用 .tsx 写的,一个是 .vue 写的,这两个组件是可以写在一个文件夹下的,并且同时在 index.ts 中进行导出;
这里需要注意的是,我还复制了 src 目录下的 vue 声明文件,因为我在任何 index.ts 中导入 vue 文件的时候,路径都爆红了,说找不到 vue 模块;
我最初以为是缺少 vue ts 声明文件,但是加上了这个声明文件路径报错依然存在;
我可以保证我已经在 vue.config.js 和 vite.config.js 和 tsconfig.js 中添加过路径相关的配置了;
后来才明白这个报错可以无视,不会影响打包效果;如果影响了,那就根本无法进行打包,所以这里先跳过吧 ovo;
import { App } from 'vue'import menu from './menu/src/index.vue'import lInfiniteMenu from './menu/src/menu.tsx'// 引入样式import './styles/common.scss'import './styles/element-plus.scss'// 最终在 views 下使用的组件,其实是这个 index.ts 中注册的组件const components = [ menu, lInfiniteMenu,] /** * 让组件可以通过 app.use() 的形式使用 * @param app */const install = (app: App): void => { app.component('l-menu', menu); app.component('l-infinite-menu', lInfiniteMenu); // components.forEach((component: any) => { // app.component(component.name, component); // });};export { menu, lInfiniteMenu,}export default { install}
8. 新建 command/build.js 实现全量打包命令配置
添加此文件时,我最初使用 require 引入 path 模块,打包的时候就报错了,说 es6 不能使用 require;
经过一番百度,我决定通过 module 模块中的 createRequire 方法,来使用 es6之前的 require;
再有一个坑:我在定义导出路径的时候,写成 src/packages,就会打包报错,写成 packages 就正常了,还没进行打印,不过如果路径报错的话,可以先尝试修改导出位置,在测试打包功能;
/** * 实现组件全局(全量)打包 * @description 因为通过 node 打包,所以采用 CommonJS 规范,也就是 require */import { createRequire } from 'module';import path from "path"const requireds = createRequire(import.meta.url);const __dirname = path.resolve();// vite 打包需要的配置const { defineConfig, build } = requireds('vite');// vite 插件const vue = requireds('@vitejs/plugin-vue');const vueJsx = requireds('@vitejs/plugin-vue-jsx');// 添加打包入口文件夹 packages(需要手动创建)const entryDir = path.resolve(__dirname, 'packages');// 添加出口文件夹 lib(不需要手动创建,会自动生成)const outDir = path.resolve(__dirname, 'lib');// vite 配置const baseConfig = defineConfig({ configFile: false, publicDir: false, plugins: [vue(), vueJsx()]});// rollup 配置(vite 基于 rollup 打包)const rollupOptions = { // 这两个库不需要打包 external: ['vue', 'vue-router'], output: { globals: { vue: 'Vue' } }};/** * 全量打包构建的方法 */const buildAll = async () => { await build({ ...baseConfig, build: { rollupOptions, lib: { // 入口 entry: path.resolve(entryDir, 'index.ts'), // 组件库名字 name: 'l-business-components', fileName: 'l-business-components', // 输出格式 formats: ['es', 'umd'] }, outDir } })};/** * 打包成库 */const buildLib = async () => { await buildAll();};buildLib();
9. 修改 package.json ,执行组件库全量打包命令
在 package.json 中添加两行命令
"build:components": "node --trace-warnings ./command/build.js",
"lib": "npm run build:components"
"scripts": { "dev": "vite", "build": "vue-tsc --noEmit && vite build", "preview": "vite preview", "build:components": "node --trace-warnings ./command/build.js", "lib": "npm run build:components" },
保存后,执行组件库全量打包命令,也就是 yarn lib,控制台输出如下:
香喷喷的组件库全量打包成功啦,生成了下面的文件
10. 在项目中使用上面打包的组件库
需要注意的是,此时组件库是全量打包,打出来的文件包含了全部组件,而不是部分组件,也就是说,还没有实现按需引入组件的功能
在 main.ts 中,引入刚刚生成的 .es.js 文件,并全局注册组件库
路径会爆红,但是不要在意,会成功的
// import LBusinessComponents from './components/index'// 使用打包后的组件库(注意此处不是按需引入,是全局引入)import LBusinessComponents from '../lib/l-business-components.es.js'import '../lib/style.css'app.use(LBusinessComponents)
这样,原来在 views 目录下使用的 组件就可以继续生效了
最开始使用打包文件的时候,报错 component render function not find,错误原因有两点:
- 配置 packages 和 components 目录下的 index.ts 时,导入组件全都没有加 .vue 文件结尾
- 封装组件的时候,使用了 setup 语法糖
按道理讲语法糖不会影响组件库打包的,目前还没研究出 为什么使用语法糖编写组件时,打包之后的组件库就渲染不出来了,components 下的组件还可以正常渲染...
希望有小伙伴可以帮忙指正!