rollup配置打包vue组件库并发布到npm
rollup配置打包vue组件库并发布到npm
在最近探索写一个vue上拉加载下拉刷新组件(组件库链接)的过程中,组件花了几周写好了,正准备打包的时候,却遇到了一个很奇怪的问题;用的是vue-cli的lib模式打包(文档在这),结果奇怪的是打包的体积竟然100k左右,但是写的组件库的代码应该只有20k,这可让人接收不了。
为了解决问题,我接着去看vue各种常用的移动端ui组件库的构建方式;vant用的是类似于vue-cli脚手架工具的vant/cli(有兴趣和时间可以去研究一下vant/cli如何构建的),还有其他组件库使用webpack打包组件库包的体积有点大,还有很多组件库用的是rollup打包组件库。
为了实现打包体积的最优化,不可能短时间内去研究vant/cli生成一套构建工具吧。经过思考,把构建工具的选型定在了rollup、parcel、vite中;最后由于rollup可配置性比较高和特别适合库和组件库的打包,最后选择了rollup,下面就来看看rollup是怎么构建打包一个可供别人使用的组件库吧。
rollup配置打包组件库
Rollup 是一个 JavaScript 模块打包器,可以将小块代码编译成大块复杂的代码,例如 library 或应用程序。在平时开发应用程序时,我们基本上选择用webpack,相比之下,rollup.js更多是用于library打包,我们熟悉的vue、react、vuex、vue-router等都是用rollup进行打包的。
rollup安装
npm i rollup -g # 全局安装
npm i rollup -D # 项目本地安装
rollup配置文件
rollup通过像webpack一样编写一个配置文件来进行打包:
在项目根目录下创建rollup.config.js,指定入口文件和打包配置:
export default {
input: './src/index.js',
output: [
{
name: 'libName',
file: './lib/index.js',
format: 'umd',
sourcemap: false,
globals: {
vue: 'vue'
}
},
{
name: 'libName',
file: './lib/index.module.js',
format: 'es',
sourcemap: false,
globals: {
vue: 'vue'
}
}
],
}
使用rollup.config.js配置文件,可以使用rollup --config或者rollup -c指令来打包。
上面打包成了umd和es两种模式,globals指定用到了的vue库;umd和es生成的打包文件还需要在package.json下面两个属性中分别指定好打包后的路径:
{
"main": "lib/index.js",
"module": "lib/index.module.js",
}
在我们的组件库发布到npm后,可以使用umd的模式调用,也可以使用es的模式;那么项目中怎么识别呢?webpack 从版本 2 开始也可以识别package.json中module 字段,如果存在 module 字段,会优先使用;es的模式也就是ES Module,带来的一个优势即是代码 tree shaking
,webpack可以对组件库中没用用到的代码打包时自动去除。
rollup插件
上面我们知道了rollup的基础用法,上面仅仅只能打包js代码;在实际应用中,会有很多更复杂的需求,比如,怎样支持es6语法,怎样打包vue文件,怎样压缩我们js的代码等等。在rollup中,我们借助插件来完成。
rollup的plugin兼具webpack中loader和plugin的功能,下面来介绍一些常用插件:
rollup-plugin-vue
rollup-plugin-vue用于处理vue文件,vue2和vue3项目所用的rollup-plugin-vue版本不一样,vue的编译器也不一样。
- vue2:rollup-plugin-vue^5.1.9 + vue-template-compiler
- vue3:rollup-plugin-vue^6.0.0 + @vue/compiler-sfc
以vue2为例:
npm i rollup-plugin-vue@5.1.9 vue-template-compiler --D
在rollup.config.js中加入rollup-plugin-vue
import vue from 'rollup-plugin-vue'
export default {
...
plugins:[
vue()
]
}
这样就可以编译、打包.vue文件了。
rollup-plugin-node-resolve
自动识别文件后缀:
import resolve from 'rollup-plugin-node-resolve';
export default {
...
plugins:[
resolve({
extensions: ['.vue', '.js']
}),
]
}
rollup-plugin-postcss
处理css需要用到的插件是rollup-plugin-postcss。它支持css文件的加载、css加前缀、css压缩、对scss/less的支持等等。
这里安装不做过多强调,差哪些包在打包时会自动提示。
import postcss from 'rollup-plugin-postcss';
export default {
...
plugins:[
postcss({
plugins: [require('autoprefixer')],
// 把 css 插入到 style 中
inject: true,
// 把 css 放到和js同一目录
// extract: true,
minimize: true,
sourceMap: false,
extensions: ['.sass', '.scss', '.less', '.css']
}),
]
}
组件库中我使用的是sass,不需要额外装其他sass包,用一个postcss就搞定;个人认为这是一个最好用的包了,既可以将css打包内联到js中,又可以单独打包css到一个css文件中。
rollup-plugin-babel
rollup-plugin-babel用于转换es6为es5语法,还需要其他比如@babel/preset-env
包来配合解析。
import babel from 'rollup-plugin-babel';
export default {
...
plugins:[
babel({
exclude: 'node_modules/**',
extensions: ['.js', '.vue']
}),
]
}
使用babel插件 @babel/preset-env、@babel/core:
新建babel.config.js:
module.exports = {
"presets": [
[
"@babel/preset-env"
]
]
}
@babel/preset-env可以转换很多es6语法,还需要转行其他es6语法则需要其他插件。
rollup-plugin-terser
rollup-plugin-terser用于压缩代码,它可以混淆和大幅度压缩代码体积。
import { terser } from 'rollup-plugin-terser';
export default {
...
plugins:[
terser(),
]
}
在package.json中配置打包命令:
"scripts": {
"build": "rollup -c"
},
npm run build就可以将./src/index.js
路径下的vue组件库打包生成lib/index.js
和lib/index.module.js
了。
- 最后将rollup用在写的上拉加载下拉刷新组件库中打包,打包后组件库的体积只有20k,然后使用rollup-plugin-terser进行代码压缩,最后竟然只有10K!这比vue-cli的lib模式打包省了好多包的体积了!!!查看源码,里面少了很多webpack无用的代码,比webpack打包纯净了很多。所以rollup真的比webpack更适合组件库的打包!而webpack更适合用在业务开发中。
下面为组件库使用rollup打包后的体积:
关于Eslint
一个组件库中,Eslint必不可少。在vue-cli中,eslint已经配置好了;那么在rollup的配置中,如何添加上eslint呢?
先安装eslint:
npm install eslint --D
安装完成后在 ./node_modules/.bin/eslint --init 或者 npx eslint --init,根据指引生成所需的 eslint 配置方案,最后生成一个 ESLint 配置文件 .eslintc.js。
当然上面配置只加了一个简单的extends, extends: eslint:recommended
经常被作为项目的JS检查规范被引入。实际项目中配置规则的时候,还有vue的规则,不可能团队一条一条的去商议配置,太费精力了。通常的做法是使用业内大家普通使用的、遵循的编码规范。 我们组件库中配置使用的是vue-cli中一样的eslint配置,直接拷贝过来就可以了。
最后配置一个eslint校验命令:
"scripts": {
"lint": "eslint ./packages --ext .vue,.js,.ts",
"lint-fix": "eslint --fix ./packages --ext .vue,.js,.ts",
}
npm run lint 就可以对指定的文件进行eslint校验,npm run lint-fix可以直接优化eslint校验不通过的代码。
关于单元测试
组件库应该保证功能的稳定性和代码质量,还有后续开源库可能存在很多人维护的问题,必须保证程序的健壮性,每次迭代保证不破坏原有的逻辑,单元测试也就尤为重要。一般组件库中做单元测试的框架有以下两种方式:
- Karma + Mocha
- Jest
其中Karma可以模拟开一个浏览器自动化测试,可以在不同的浏览器里跑样式测试等;Macha提供一个自动化测试的框架,用于单元测试的断言。
Jest集成了Mocha + jsdom(node环境模拟dom环境,无法测试样式)自带测试覆盖率,优点是零配置,包含了测试框架的所有内容,还带有快照等功能,开箱即用。
单元测试中我选择的是Jest,主要是配置很简单,还自带测试覆盖率。
配置Jest测试vue组件
安装:
npm install jest babel-jest vue-jest @vue/test-utils -D
配置Jest:
// jest.config.js
module.exports = {
"testMatch": ["**/test/*.spec.[jt]s?(x)"], // Jest 测试的文件
'moduleFileExtensions': [
'js',
// 告诉 Jest 处理 `*.vue` 文件
'vue'
],
'transform': {
// 用 `vue-jest` 处理 `*.vue` 文件
'.*\\.(vue)$': 'vue-jest',
// 用 `babel-jest` 处理 js
'.*\\.(js)$': 'babel-jest'
}
}
Jest常用API
Jest提供了全局函数:
- describe(name, fn) 把相关测试组合在一起
- test(name, fn) 测试方法
- expect(value) 断言
匹配器:
- toBe(value) 判断值是否相等
- toEqual(obj) 判断对象是否相等
- toContain(value) 判断数组或字符串是否包含
快照:
- toMatchSnapshot() 第一次调用会把expect中的值以字符串形式存储在文本文件中,后面再运行快照则会对比文本文件中的字符串和expect中的值,如果成功,则测试通过,否则测试不通过。
Vue Test Utils 常用API
- mount()
创建一个vue组件的wrapper包裹器,这个包裹器除了包含组件实例外,还提供了对组件Dom操作的方法等等...
-
wrapper包裹器常用方法
- vm 组件实例
- props 实例选项中的props
- html() 组件生成的html标签
- trigger() 触发Dom原生事件,如果触发自定义事件,则为wrapper.vm.$emit()
- find() 查找dom元素
使用Jest + @vue/test-utils测试组件库:
Jest是一个测试框架,内置了断言,看看怎么和@vue/test-utils结合测试组件。
比如说我们测试一个input组件传入type="password"是否生效
import input from 'src/input.vue'
import { mount } from '@vue/test-utils'
test('input-password', () => {
const wrapper = mount(input, {
propsData: {
type: 'password'
}
})
expect(wrapper.html()).toContain('input type="password"')
})
测试传入value,生成快照,查看快照内容和预期是否一致
test('input-snapshot', () => {
const wrapper = mount(input, {
propsData: {
type: 'text',
value: 111
}
})
expect(wrapper.vm.$el).toMatchSnapshot()
})
以上就是单元测试的简单使用。
关于持续集成
持续集成就是把代码测试、打包、发布等工作交给一些工具来自动完成。这样可以提高效率,开发人员只需要关心开发和提交代码到git就可以了。
Github Actions是Github官方提供的一个非常好用的持续集成工具,我们可以用Github Actions来做发布、持续单元测试等。
发布到npm
如果我们在业务中写了组件库抽离出来,想要分享出去给别人使用,一般都会把它作为 npm 包发布到官方仓库中,需要使用的时候再通过 npm install xxx 来安装即可。
那么如何发布一个 npm 包呢?
首先要指定好package.json里面的必须字段:
{
"name": "your package name",
"version": "1.0.0",
"description": "",
"main": "index.js",
"keywords": [],
"author": "",
"license": "MIT"
}
其中name写好包名,必须指定一个初始版本version,然后指定main入口文件,这些都是必不可少的。
"main"的作用是别人import、require的时候,引用的文件,所以main指定为rollup打包后的文件路径。
一切组件库工作准备就绪后,就可以通过下面npm命令发布到npm了。
npm addUser # 假若没有账号,则注册一个
npm login # 假若已经有账号了,则直接登录
npm publish # 将组件库发布到npm
后续
整个组件库的架构还不够完善,在后续工作中,还有很多事情需要做;比如很有必要添加单元测试等,组件库的功能更新,支持vue3、TS等,这些有时间会在后续补上,有兴趣的可以关注我这个上拉加载下拉刷新的组件库。别急,让子弹先飞一会...
参考资料(Tanks):
转载自:https://juejin.cn/post/7043399990382690334