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

本文介绍了将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 的导出方案,组件库导出的组件依赖,要提供每个组件单独打包的依赖文件。

全量导出 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 配置。

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

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'
  }
}
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;
}

项目中的组件目录为如下,配置将会将每个组件打包编译导出到 lib 中

components             组件文件目录
│
│— button
│  │— button.vue         button组件
│  └─ index.js          button组件导出文件
│
│— input
│  │— input.vue          input组件
│  └─ index.js          input组件导出文件
│
│— musicPlayer
│  │— musicPlayer.vue       musicPlayer组件
│  └─ index.js          musicPlayer组件导出文件
│
│ base.js             基础组件的导出文件
└─ index.js            所有组件的导出文件

lib                编译后的文件目录
│
│— button
│  │— style.css          button组件依赖样式
│  └─ index.js          button组件依赖文件
│
│— input
│  │— style.css          input组件依赖样式
│  └─ index.js          input组件依赖文件
│
│— music-player
│  │— style.css          musicPlayer组件依赖样式
│  └─ index.js          musicPlayer组件依赖文件
│
│— base
│  │— style.css          基础组件依赖样式
│  └─ index.js          基础组件依赖文件
│
└─ index
  │— style.css          所有组件依赖样式
  └─ index.js          所有组件依赖文件

获取组件全部入口时,对入口名称做驼峰转横杠处理 upperCasetoLine,是因为 babel-plugin-import 在按需引入时,如组件名称为驼峰命名,路径会转换为横杠分隔。

例如业务系统引入

import { MusicPlayer } from "jr-ui"

// 转化为
var MusicPlayer = require('jr-ui/lib/music-player');
require('jr-ui/lib/music-player/style.css');

因为组件库命名约定,组件文件夹命名大小写并不以横杠隔开。但为了让 babel-plugin-import 正确运行,所以此处对每个文件的入口文件名称做了转换处理。

如不经过方法转换名称,也可以配置 babel.config.js 中的plugin-import配置 camel2DashComponentName 为 false,来禁用名称转换。

babel-plugin-import路径命名issue

业务系统使用时

全量导出默认导出全部组件

// 全量导出
import JRUI from "jr-ui";
import "jr-ui/lib/index/index.css";

Vue.use(JRUI);

基础导出仅导出基础组件,如需要使用额外组件,需要安装 babel-plugin-import 插件且配置 babel.config.js 来完成导入语句的转换

npm i babel-plugin-import -D

业务系统——babel.config.js配置

module.exports = {
 presets: ["@vue/app", ["@babel/preset-env", { "modules": false }]],
 plugins: [
  [
   "import",
   {
    "libraryName": "jr-ui",
    "style": (name) => {
      return `${name}/style.css`;
    }
   }
  ]
 ]
}

基础导出

import JRUI_base from "jr-ui/lib/base";
import "jr-ui/lib/base/index.css";
Vue.use(JRUI_base);

// 按需使用额外引入的组件
import { MusicPlayer } from "jr-ui";
Vue.use(MusicPlayer);

业务系统中调试组件库代码

如果仍然想调试组件库代码,在引入组件时,直接引入组件库依赖内的 components 下的组件导出文件并覆盖安装。就能调试目标组件。

import button from "jr-ui/src/components/button";
Vue.use(button);

优化效果

在组件库较大的情况下,优化效果非常明显。在使用基础组件时,体积小了一兆。而且还减少了很多组件内不必要的第三方依赖文件资源。

案例仓库地址,如有疑问和错误的地方,欢迎大家提问或指出。
祝你有个快乐的劳动节假期 :)
Have a nice day.

参考资料

vue-cli执行解析
babel-plugin-import

到此这篇关于将Vue组件库更换为按需加载的方法步骤的文章就介绍到这了,更多相关Vue组件库更换为按需加载内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 教你搭建按需加载的Vue组件库(小结)

    按需加载的原理 按需加载,本质上是把一个组件库的不同组件 拆分成不同文件 ,按照需要引用对应的文件,而该文件暴露一个 install方法 ,供Vue.use使用. 比如:我只想引用element库里的一个Button组件 import Button from 'element-ui/lib/Button.js' import Button from 'element-ui/lib/theme-chalk/Button.css' Vue.use(Button); 上面的写法比较繁琐,而且需要知道每

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

    本文介绍了将Vue组件库更换为按需加载的方法步骤,分享给大家,具体如下: 按需加载DEMO仓库地址 背景 我司前端团队拥有一套支撑公司业务系统的UI组件库,经过多次迭代后,组件库体积非常庞大. 组件库依赖在npm上管理,组件库以项目根目录的 index.js 作为出口导出,文件中导入了项目中所有的组件,并提供组件安装方法. index.js import Button from "./button"; import Table from "./table"; imp

  • react脚手架如何配置less和ant按需加载的方法步骤

    前言 create-react-app是由React官方提供并推荐使用构建新的React单页面应用程序的最佳方式,其构建的项目默认是不支持less的,需要我们手动集成 一.react脚手架搭建 1.先全局安装create-react-app(提前需要安装node) npm install -g create-react-app 2.然后通过create-react-app创建项目my-app create-react-app my-app 3.最后通过cd进入项目文件夹并启动 cd my-app

  • Vue.js 的移动端组件库mint-ui实现无限滚动加载更多的方法

    通过多次爬坑,发现了这些监听滚动来加载更多的组件的共同点, 因为这些加载更多的方法是绑定在需要加载更多的内容的元素上的, 所以是进入页面则直接触发一次,当监听到滚动事件之后,继续加载更多, 所以对于无限滚动加载不需要写首次载入列表的函数, 代码如下: html: //父组件 <div v-infinite-scroll="loadMore" infinite-scroll-disabled="loading" infinite-scroll-distance=

  • Winform中Treeview实现按需加载的方法

    本文实例讲述了Winform中Treeview实现按需加载的方法,非常具有实用价值.分享给大家供大家参考.具体分析如下: 最近项目里用到treeview,原先设计的是一开始就把所有数据都加载到treeview里,后来发现客户的数据量实在太大,加载所有数据要2分钟,这个是客户没法接受的.后来就考虑到用户也不是一开始就要看所有的数据,用户也是一层一层地展开,所以我们就考虑是不是可以实现以当用户展开某个结点时才加载当前结点下面的数据.一番查找后,发现treeview有BeforeExpand事件可以实

  • react 实现页面代码分割、按需加载的方法

    虽然一直有做 react 相关的优化,按需加载.dll 分离.服务端渲染,但是从来没有从路由代码分割这一块入手过,昨天在本地开发时没有测试成功,今天又搞了下,已经部署到线上环境了,今天就这个记录一下. 修改配置 开发环境:webpack@v3 .react-router@v4 安装依赖: $ yarn add babel-plugin-syntax-dynamic-import -dev 修改 .babelrc 文件:在 plugins 中添加 "syntax-dynamic-import&qu

  • iview在vue-cli3如何按需加载的方法

    iview在官方文档上,对于使用脚手架vue-cli3的项目的使用只有一句话:"我们为最新的 Vue CLI 3 提供了相应的 iView 插件,如果你正在用 Vue CLI 3,可以直接在插件中搜索 iview,安装插件来使用." 老实说,第一次看到这说明,我是懵逼的--废话不多说,直接撩起袖子撸 vue-cli3有个命令vue ui打开添加插件搜索 vue-cli-plugin-iview,点击安装 按需加载安装后,vue-cli-plugin-iview会自动帮我们做好以下的配置

  • 易语言给组件上加载图片方法步骤

    易语言中,我们如何将喜欢的图片放到窗口或者组件上呢? 下面笔者为大家讲解. 1.首先,我们新建一个易语言Windows窗口程序. 如图所示. 2.在窗口中,我们放置好如下如组件. 我们先将如何在按钮上加载图片. 点击按钮,在左侧属性中寻找图片选项. 如图所示 3.点击图片选项,出现如图所示界面. 再此界面上我们需要加载图片 4.选择更换按钮,在你本机上找到你要更换的图片,点击确定即可 如图所示 5.下面,我们来讲解另一种加载图片的方法. 首先,我们在资源表中找到图片选项,进入.如图所示 6.选择

  • Vue下滚动到页面底部无限加载数据的示例代码

    看到一篇Implementing an Infinite Scroll with Vue.js, 觉得挺实用的就看了下, 顺便简单翻译了一下给需要的人参考. 从这个项目中可以加深对Vue的生命周期的理解, 何时开始axios请求, 如何结合Vue使用原生js来写scroll事件等等, 我这里主要是对原文的重点提取和补充 本文技术要点 Vue生命周期 axios简单用法 moment.js格式化日期 图片懒加载 结合原生js来写scroll事件 请求节流 创建项目 首先创建一个简单的vue项目 #

  • jQuery 判断元素是否存在然后按需加载内容的实现代码

    前端开发的过程中,我们会使用很多 jQuery 插件,jQuery 插件使用得多了,会导致网页打开速度变慢.而引入的 jQuery 插件并不是每个页面都需要的.这时候使用按需加载的方法加载 jQuery 插件会对前端性能的提升有不少帮助.按需加载的方法有很多,今天我们来说一下 jQuery 的方法.判断网页中一个元素是否存在的方法: var $selector = $('.my-element'); if ( $selector.length > 0 ) { // 如果存在,引入jQuery库,

随机推荐