如何快速使用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实例上才能正常使用。

相关推荐
也无晴也无风雨41 分钟前
深入剖析输入URL按下回车,浏览器做了什么
前端·后端·计算机网络
Martin -Tang1 小时前
Vue 3 中,ref 和 reactive的区别
前端·javascript·vue.js
FakeOccupational3 小时前
nodejs 020: React语法规则 props和state
前端·javascript·react.js
放逐者-保持本心,方可放逐3 小时前
react 组件应用
开发语言·前端·javascript·react.js·前端框架
曹天骄4 小时前
next中服务端组件共享接口数据
前端·javascript·react.js
阮少年、4 小时前
java后台生成模拟聊天截图并返回给前端
java·开发语言·前端
郝晨妤5 小时前
鸿蒙ArkTS和TS有什么区别?
前端·javascript·typescript·鸿蒙
AvatarGiser6 小时前
《ElementPlus 与 ElementUI 差异集合》Icon 图标 More 差异说明
前端·vue.js·elementui
喝旺仔la6 小时前
vue的样式知识点
前端·javascript·vue.js
别忘了微笑_cuicui6 小时前
elementUI中2个日期组件实现开始时间、结束时间(禁用日期面板、控制开始时间不能超过结束时间的时分秒)实现方案
前端·javascript·elementui