当前环境: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实例上才能正常使用。