如何快速使用vue-cli-service打包一个支持按需引用的组件库?

当前环境:vue@2.6.14、@vue/cli-service~5.0.0

以二次封装Element-UI的button和alert组件为例,进行记录。

1、构建项目,开发组件库

首先使用vue create [projectName]命令初始化一个工程,在src同级目录下创建一个packages文件夹用于存放我们开发的组件。结构如下:

2、打包

按需引用与普通的直接引入组件库有啥区别?

直接引入组件库,就是我们常用的import ElementUI from 'element-ui',引入并use后,全局都能使用element-ui的组件,问题是,不管你使用了几个element-ui的组件,当你访问系统时,他都会加载所有的组件库文件,影响首屏加载的性能,也增加了包的大小。

按需引用就是用几个组件就加载几个组件,去掉了多余的代码和文件,减小了包的体积,提升了首屏加载速度,常见引用方式比如import { Button } from 'element-ui',当前,这个引用方式是为了方便书写,是babel-plugin-component为我们做了简化之后的写法,他的全貌应该是import { Button } from 'element-ui/lib/button.js,如果不配置babel-plugin-component就使用简化写法是不会进行按需引用的。另外,以element-ui为例,如果直接引入组件库是需要再单独引入css文件的,但是按需引入的话就不需要了,因为babel-plugin-component会在解析时给我们自动加上。

通过使用上的区别,我们可以大概分析出两种使用方式在打包时的区别,普通引入方式,在打包时只需要将所有组件统一导出,提供统一入口文件进行打包即可。如果要支持按需引入方式,那就需要对每个组件分别打包,这样才能跟其他组件区分开来,在使用这个组件的时候不会去加载另一个组件。

具体的开发方式及打包代码如下:

首先做支持普通引入方式的开发

packages/q-button/index.vue中编写如下代码

packages/q-alert/index.vue中编写如下代码

注意组件库文件中需要引入各自需要的组件并注册。

组件开发完成后统一在packages/index.js中导出,作为打包的入口文件,代码如下:

install是用于业务库中使用Vue.use时,将组件都全局注册到Vue实例下。

代码开发完成后,开始打包,修改package.json的build脚本

--target指定为lib模式;默认打包后的文件存到dist文件夹下,也可以通过--dest修改;packages/index.js为打包入口文件。

最后输入文件如下:

发布到npm上就可以使用了。

接下来讲述支持按需引入的打包方式

按需打包的开发上和打包配置上都是有要求的。 首先是开发上,开发组件的文件index.vue都是相同的,但这个时候组件文件夹下的index.js就派上用场了,代码如下

他给每个组件都设置了单独的install方法,所以按需引用的时候可以单独进行Vue.use()。

接下来是打包配置上,本质讲就是能够按照babel-plugin-component要求的格式进行多入口打包,可以使用gulp来用vue-cli-service命令打包,也可以用webpack打包,这里使用的是webpack。

vue.config.js代码如下

javascript 复制代码
const path = require('path')
const join = path.join;
const fs = require('fs');

function resolve(dir) { //获取绝对路径
  return path.resolve(__dirname, dir)
}
// 获取文件夹下所有index.js的绝对路径
function getEntries(path) {
  let files = fs.readdirSync(resolve(path));
  const entries = files.reduce((ret, item) => {
    const itemPath = join(path, item)
    const isDir = fs.statSync(itemPath).isDirectory();
    if (isDir) {
      ret[item] = resolve(join(itemPath, 'index.js'))
    } else {
      const [name] = item.split('.')
      ret[name] = resolve(`${itemPath}`)
    }
    return ret
  }, {})
  return entries
}

const devConfig = { // 开发配置
  lintOnSave: false,
  pages: {
    index: {
      entry: 'src/main.js', // 入口文件
      template: 'public/index.html',
      filename: 'index.html'
    }
  },
  configureWebpack: {
    resolve: {
      extensions: ['.js', '.vue', '.json'],
      alias: { // 别名
        "@": resolve('src'),
      }
    },
    devtool: 'source-map'
  },
}

const buildConfig = { // 打包配置
  outputDir: 'lib', // 输出文件夹名
  publicPath: 'lib',
  productionSourceMap: false, // 禁止打包生成源码映射
  css: {
    sourceMap: true,
    extract: {
      filename: '[name]/style.css' // 在lib文件夹中建立style文件夹中,生成对应的css文件。
    }
  },
  
  configureWebpack: {
    mode: 'production',
    entry: {
      ...getEntries('packages') // 入口文件
    },
    output: { // 出口文件
      filename: '[name]/index.js', // 文件名
      libraryTarget: 'commonjs2',
    },
    optimization: {
      // usedExports: true,
      minimize: false
    },
  },
  chainWebpack: config => {
    // 在生产环境下也要将新增的packages文件夹加入babel转码编译
    config.module
      .rule('js')
      .include
      .add(resolve('packages'))
      .end()
      .use('babel')
      .loader('babel-loader')
      .tap(options => {
        return options
      })
    // 删除Vue CLI3原先打包编译的一些无用功能
    config.optimization.delete('splitChunks') // 删除splitChunks,因为每个组件是独立打包,不需要抽离每个组件的公共js出来
    config.plugins.delete('copy') // 删除copy,不要复制public文件夹内容到lib文件夹中。
    config.plugins.delete('html') // 删除html,只打包组件,不生成html页面。
    config.plugins.delete('preload') 
    config.plugins.delete('prefetch') // 删除preload以及prefetch,因为不生成html页面,所以这两个也没用。
    config.plugins.delete('hmr') // 删除hmr,删除热更新。
    config.entryPoints.delete('app') // 删除自动加上的入口App。

  }
}

module.exports = process.env.NODE_ENV === 'development' ? devConfig : buildConfig // 判断环境变量使用相应的配置

最终目的就是打出一个如此结构的包

最后配置package.json的build脚本,去掉多余属性,因为验证的时候发现如果有属性,打包时会忽略vue.config.js的部分配置。

打包完成后发布npm即可,main入口为:

猜测点:使用import还是Vue.use?

在涉及到第三方库的封装时,有两种设置方式,第一种是文中描述的方式,在每个组件库中引入自己需要的组件,这样打包的时候就不会出现找不到包的情况。

还有一种方式是可以通过Vue.use()来注册第三方库

但是在index.js中我们自己的组件和第三方库都需要use 才能正常使用,这个具体原因尚未搞明确,猜测可能是需要都关联到同一个vue实例上才能正常使用。

相关推荐
@大迁世界7 分钟前
TypeScript 的本质并非类型,而是信任
开发语言·前端·javascript·typescript·ecmascript
GIS之路15 分钟前
GDAL 实现矢量裁剪
前端·python·信息可视化
是一个Bug19 分钟前
后端开发者视角的前端开发面试题清单(50道)
前端
Amumu1213821 分钟前
React面向组件编程
开发语言·前端·javascript
持续升级打怪中43 分钟前
Vue3 中虚拟滚动与分页加载的实现原理与实践
前端·性能优化
GIS之路1 小时前
GDAL 实现矢量合并
前端
hxjhnct1 小时前
React useContext的缺陷
前端·react.js·前端框架
冰暮流星1 小时前
javascript逻辑运算符
开发语言·javascript·ecmascript
前端 贾公子1 小时前
从入门到实践:前端 Monorepo 工程化实战(4)
前端
菩提小狗1 小时前
Sqlmap双击运行脚本,双击直接打开。
前端·笔记·安全·web安全