将Vue组件库更换为按需加载的方法步骤

按需加载DEMO仓库地址

背景

我司前端团队拥有一套支撑公司业务系统的UI组件库,经过多次迭代后,组件库体积非常庞大。

组件库依赖在npm上管理,组件库以项目根目录的 index.js 作为出口导出,文件中导入了项目中所有的组件,并提供组件安装方法。

index.js

import Button from "./button"; import Table from "./table"; import MusicPlayer from "./musicPlayer"; import utils from "../utils" import * as directive from "../directive"; import * as filters from "../filters"; const components = { Button, Table, MusicPlayer } const install = (Vue) => { Object.keys(components).forEach(component => Vue.use(component)); // 此处继续完成一些服务的挂载 } if (typeof window !== 'undefined' && window.Vue) { install(Vue, true); } export default { install, ...components }

组件库并不导出编译完成后的依赖文件,业务系统使用时,安装依赖并导入,就能注册组件。

import JRUI from 'jr-ui'; Vue.use(JRUI);

组件库的编译是交由业务系统的编译服务顺带编译的。

即组件库项目本身不会编译,仅作为组件导出。node_module 就像一个免费的云盘,用于存储组件库代码。

因为经业务系统编译,在业务系统中。组件库代码能够和本地文件一样,直接调试。而且非常简单粗暴,并不需要做一些依赖导出的额外配置。

但也存在缺点

组件库中无法使用更为特殊的代码

vue-cli会静态编译在 node_module 引用的 .vue 文件,但不会编译 node_module 中的其他文件,一旦组件库代码存在特殊的语法扩展(JSX),或者特殊的语言(TypeScript)。此时项目启动会运行失败。

组件库中使用 webpack 的特殊变量将不起效

组件库中的 webpack 配置不会被业务系统去执行,所以组件库中的路径别名等属性无法使用

组件库依赖每次都是全量加载

index.js 本身就是全量的组件导入,所以即使业务系统只使用了部分组件, index.js 也会将所有的组件文件(图片资源,依赖)都打包进去,依赖体积总是全量大小的。

业务系统并不存在只使用一两个组件的情况,每个业务系统都需要绝大部分组件。
几乎每个项目都会使用比如 按钮,输入框,下拉选项,表格 等常见基础组件。
只有部分组件仅在少数特殊业务线使用,例如 富文本编辑器,音乐播放器。

组件分类

为了解决上述问题,及完成按需引入的效果。提供两种组件导出方式,全量导出,基础导出。
将组件导出分为两种类型。基础组件,按需引入组件。
按需引入组件的评定标准为:

较少业务系统使用

组件中包含体积较大或资源文件较多的第三方依赖

未被其他组件内部引用

全量导出模式导出全部组件,基础导出仅导出基础组件。在需要使用按需引入组件时,需要自行引入对应组件。

调整为按需引入

参考 element-ui 的导出方案,组件库导出的组件依赖,要提供每个组件单独打包的依赖文件。

将Vue组件库更换为按需加载的方法步骤

全量导出 index.js 文件无需改动,在 index.js 同级目录增加新文件 base.js,用于导出基础组件。

base.js

import Button from "./Button"; import Table from "./table"; const components = { Button, Table } const install = (Vue) => { Object.keys(components).forEach(component => Vue.use(component)); } export default { install, ...components }

修改组件库脚手架工具,增加额外打包配置。用于编译组件文件,输出编译后的依赖。

vue.config.js

const devConfig = require('./build/config.dev'); const buildConfig = require('./build/config.build'); module.exports = process.env.NODE_ENV === 'development' ? devConfig : buildConfig;

config.build.js

const fs = require('fs'); const path = require('path'); const join = path.join; // 获取基于当前路径的目标文件 const resolve = (dir) => path.join(__dirname, '../', dir); /** * @desc 大写转横杠 * @param {*} str */ function upperCasetoLine(str) { let temp = str.replace(/[A-Z]/g, function (match) { return "-" + match.toLowerCase(); }); if (temp.slice(0, 1) === '-') { temp = temp.slice(1); } return temp; } /** * @desc 获取组件入口 * @param {String} path */ function getComponentEntries(path) { let files = fs.readdirSync(resolve(path)); const componentEntries = files.reduce((fileObj, item) => { // 文件路径 const itemPath = join(path, item); // 在文件夹中 const isDir = fs.statSync(itemPath).isDirectory(); const [name, suffix] = item.split('.'); // 文件中的入口文件 if (isDir) { fileObj[upperCasetoLine(item)] = resolve(join(itemPath, 'index.js')) } // 文件夹外的入口文件 else if (suffix === "js") { fileObj[name] = resolve(`${itemPath}`); } return fileObj }, {}); return componentEntries; } const buildConfig = { // 输出文件目录 outputDir: resolve('lib'), // webpack配置 configureWebpack: { // 入口文件 entry: getComponentEntries('src/components'), // 输出配置 output: { // 文件名称 filename: '[name]/index.js', // 构建依赖类型 libraryTarget: 'umd', // 库中被导出的项 libraryExport: 'default', // 引用时的依赖名 library: 'jr-ui', } }, css: { sourceMap: true, extract: { filename: '[name]/style.css' } }, chainWebpack: config => { config.resolve.alias .set("@", resolve("src")) .set("@assets", resolve("src/assets")) .set("@images", resolve("src/assets/images")) .set("@themes", resolve("src/themes")) .set("@views", resolve("src/views")) .set("@utils", resolve("src/utils")) .set("@mixins", resolve("src/mixins")) .set("jr-ui", resolve("src/components/index.js")); } } module.exports = buildConfig;

此时我们的 npm run build 命令,执行的便是以上这段 webpack 配置。

配置中,会寻找组件目录的所有入口文件。对每个入口文件根据设置进行编译输出到指定路径。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/e1beff54e5475e986351b76afe3613f5.html