Rollup是一个轻量级javascript模块打包器。相比于Webpack,Rollup更适合打包library。Rollup基于ES6模块,ES模块允许通过静态分析,实现tree-shaking优化,删除冗余代码。
对于Rollup和Webpack的选择,可以看下Rollup的官方文档:
Rollup 是用来构建库还是应用程序?(Is Rollup meant for building libraries or applications?) Rollup 已被许多主流的 JavaScript 库使用,也可用于构建绝大多数应用程序。但是 Rollup 还不支持一些特定的高级功能,尤其是用在构建一些应用程序的时候,特别是代码拆分和运行时态的动态导入 dynamic imports at runtime. 如果你的项目中更需要这些功能,那使用 Webpack可能更符合你的需求。
本文记录了打包一个js组件,并发布到npm的过程。
这次示例我们打包一个基于mobile-select封装的城市选择组件,先来看下将要被打包的代码。
代码语言:javascript复制import CityList from './list'
import MobileSelect from 'mobile-select'
const CitySelector = {}
CitySelector.districtData = null
CitySelector.instance = null
CitySelector.callback = null
/**
* 初始化方法
* @param trigger 省份城市控件id
* @param title 标题
* @param callback (indexArr, data) data是一个数组
* @param type 类型 1 选择省市 2 选择省市区
*/
CitySelector.init = function (trigger, callback, title = "请选择省份城市", type = 1) {
CitySelector.callback = callback
CitySelector.districtData = CityList
CitySelector.doInit(trigger, title, type)
}
// ***
export default CitySelector
可以看到,我们的代码import了两个外部库,list是存在本地的城市区划列表,mobile-select是一个开源的选择弹框组件。
1. 初始化项目
代码语言:javascript复制npm init
会生成一个package.json文件,修改下配置。
代码语言:javascript复制{
"name": "tip-city-selector",
"version": "1.0.5",
"description": "cn city selection component",
"main": "dist/tip-city-selector.js",
"scripts": {
"build": "rollup -c"
},
"author": "luciozhang",
"license": "ISC",
"devDependencies": {},
"dependencies": {}
}
2. ESLint代码检查
通过代码规范,避免不必要的bug。
代码语言:javascript复制npm install --save-dev eslint
3. 安装Rollup
代码语言:javascript复制npm install --global rollup
4. 安装Babel相关插件
为了使用JavaScript的新特性。
https://www.rollupjs.com/guide/tools/#babel
关于babel的使用,推荐阅读下面这篇文章:
你真的会用 Babel 吗?
代码语言:javascript复制npm i -D rollup-plugin-babel
rollup-plugin-babel插件并不包含babel包,需要安装必要的babel包依赖。
代码语言:javascript复制npm i -D @babel/core @babel/preset-env
配置rollup.config.js
代码语言:javascript复制// rollup.config.js
import babel from 'rollup-plugin-babel';
export default {
input: 'src/main.js', //打包入口
output: {
file: 'bundle.js', //输出文件
format: 'cjs' //模块化方式
},
plugins: [
babel({
exclude: 'node_modules/**' // 只编译我们的源代码,忽略第三方代码
})
]
};
创建babel配置.babelrc
代码语言:javascript复制//.babelrc
{
"presets": [
["latest", {
"es2015": {
"modules": false //设置"modules": false,否则 Babel 会在 Rollup 有机会做处理之前,将我们的模块转成 CommonJS,导致 Rollup 的一些处理失败
}
}]
],
"plugins": ["external-helpers"] //允许 Rollup 在包的顶部只引用一次 “helpers”,而不是每个使用它们的模块中都引用一遍(这是默认行为)
}
安装插件babel-preset-latest和babel-plugin-external-helpers
代码语言:javascript复制npm i -D babel-preset-latest babel-plugin-external-helpers
运行一下,rollup -c。
报错了!
代码语言:javascript复制[!] (plugin babel) Error: /Users/luciozhang/tip-city-selector/src/main.js: Babel 7.0.0-beta.56 has dropped support for the 'helpersNamespace' utility.If you are using @babel/plugin-external-helpers you will need to use a newer version than the one you currently have installed. If you have your own implementation, you'll want to explore using 'helperGenerator' alongside 'file.availableHelper()'.
看了下rollup-plugin-babel的issues,原来Babel7.x版本不建议使用babel-plugin-external-helpers,也没对其进行支持。
对比了下英文文档,是中文文档翻译不及时的锅。
我们修改下.babelrc文件去掉babel-preset-latest babel-plugin-external-helpers两个插件。
代码语言:javascript复制//.babelrc
{
"presets": [
["@babel/env", {"modules": false}]
]
}
5. Node 模块位置解析
再次运行rollup -c。
可以了!!
但发现一个WARN
代码语言:javascript复制(!) Missing global variable name
Use output.globals to specify browser global variable names corresponding to external modules
mobile-select (guessing 'MobileSelect')
我们已经安装了mobile-select模块,但没有被找到。
我们需要rollup-plugin-node-resolve 插件,来告知Rollup怎么查找外部模块,安装它
代码语言:javascript复制npm install -D rollup-plugin-node-resolve
代码语言:javascript复制//rollup.config.js
plugins: [
// …
resolve({
browser: true,
}),
// …
]
6. 加载 CommonJS 模块
再再次运行rollup -c。
WARN没了。但出现了一个Error。
代码语言:javascript复制[!] Error: 'default' is not exported by node_modules/mobile-select/mobile-select.js
https://rollupjs.org/guide/en/#error-name-is-not-exported-by-module
mobile-select模块是CommonJS模块,在Rollup中,加载CommonJS模块的能力放在可选插件中,我们需要安装rollup-plugin-commonjs。
代码语言:javascript复制npm install -D rollup-plugin-commonjs
代码语言:javascript复制//rollup.config.js
plugins: [
// …
commonjs({
include: ['node_modules/**']
}),
// …
]
7. PostCSS插件
再再再次运行rollup -c。
上一个Error解决了,我们来看下新的Error
代码语言:javascript复制!] Error: Unexpected token (Note that you need plugins to import files that are not JavaScript)
node_modules/mobile-select/mobile-select.css (1:0)
Rollup本身不支持加载css组件,我们需要安装一个Rollup的PostCSS插件。
代码语言:javascript复制npm install -D rollup-plugin-postcss
代码语言:javascript复制//rollup.config.js
plugins: [
// …
postcss({
extensions: ['.css'],
}),
// …
]
最后一次运行rollup -c,可以了。
8. 压缩代码
打包出来的js文件比较大,我们用rollup-plugin-terser压缩下代码。
代码语言:javascript复制npm install -D rollup-plugin-terser
在rollup.config.js的 插件列表加上terser()。
9. Rollup输出配置
打包的工作到这里就结束了,我们想要测试打包成果。
可能你还记得,我们打包输出的是CommonJS模块,因为rollup.config.js里面的output.format选项选择的是cjs。
可选的输出模块类型有下面这些:
- amd – 异步模块定义,用于像 RequireJS 这样的模块加载器。
- cjs – CommonJS,适用于 Node 和 Browserify/Webpack。
- es – 将软件包保存为 ES 模块文件。
- iife – 一个自动执行的功能,适合作为
<script>
标签。(如果要为应用程序创建一个捆绑包,您可能想要使用它,因为它会使文件大小变小。) - umd – 通用模块定义,以 amd,cjs 和 iife 为一体。
为了放在<script>
中使用,方便测试,我们将打包格式改为umd。
同时,对于life和umd格式,我们需要指定输出模块名称,在js脚本中可以直接使用模块。
最终,我们的Rollup打包配置是这样的。
代码语言:javascript复制//rollup.config.js
import { terser } from 'rollup-plugin-terser';
import babel from 'rollup-plugin-babel';
import resolve from 'rollup-plugin-node-resolve';
import commonjs from 'rollup-plugin-commonjs'
import postcss from 'rollup-plugin-postcss';
export default {
input: './src/main.js',
output: {
file: './dist/tip-city-selector.js',
format: 'umd',
name: 'CitySelector'
},
runtimeHelpers: false,
plugins: [
postcss({
extensions: ['.css'],
}),
resolve({
browser: true,
}),
commonjs({
include: ['node_modules/**']
}),
babel({
exclude: 'node_modules/**',
}),
terser(),
]
};
10. 发布npm
最后,如果对封装的组件有信心的话,我们可以把组件发布到npm
package.json的main属性指向包的出口,在我们的项目中,是"main": "dist/tip-city-selector.js"。
npm login登录,npm publish发布。
0. 参考文章
Rollup官方文档
你真的会用 Babel 吗?
rollup-plugin-babel