Rollup是一个JavaScript的模块化打包工具,可以帮助我们编译小的代码到一个大的、复杂的代码中,比如一个库或者一个应用程序
- Rollup也是一个模块化的打包工具
- rollup默认情况下只能对ESM进行打包
- Rollup的核心系统是插件系统
- 通过rollup的插件,rollup可以打包CJS模块,css文件等
- Rollup可以打包JS库和应用程序,但更多的还是用于构建JS库
- rollup支持tree shaking
- 相比webpack,rollup构建后的文件更为的精简
应用场景
- 构建JS库,如dayjs,vue,react等
- vite在构建阶段 底层所使用的构建工具就是rollup
安装
npm install rollup -g
npm install rollup
运行
CLI
# npx rollup <入口文件> -o <出口文件>
# 在rollup构建中需要指定运行环境,即输出代码需要运行在那种环境下
# 否则rollup并不会对代码进行构建,例如如下指令,rollup会直接将输入的index.js原封不动的输出的bundle.js
$ npx rollup ./src/index.js -o ./dist/bundle.js
# 可以通过-f 参数 来指定输出环境
# -f cjs --- 运行在commonjs环境 如node
# -f iife --- 运行在browser环境
# -f es --- 运行在ESM环境
# -f amd --- 运行在AMD环境
# -f umd --- UMD(Universal Module Definition)模式,也就是通用模式
# --- 可以直接运行在CJS, browser和AMD模式下,但不支持ESM模式
# 如果输出模式为iife 需要为导出内容取一个全局名称
# 这个代码的功能是为了使用这可以在浏览器中使用模块
# 这个全局名称的功能类似于 jQuery中的$和jQuery 或者说是 lodash中的_
# 如果输出模式为umd,那么就必须为导出内容取一个全局名称,否则会报错
$ npx rollup ./src/index.js -f umd -o ./dist/bundle.js --name=util
# 新版的执行代码如下
$ rollup ./src/index.js --file ./dist/bundle.js --format umd --name "util"
// UMD导出文件格式 --- 伪代码
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.util = {}));
})(this, (function (exports) { 'use strict';
function foo() {
console.log('foo');
}
exports.foo = foo;
}));
// 上述代码等价于
const baz = function (global, factory) {
// 存在exports和module 所以是CJS环境
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
// 存在define所以是AMD环境
typeof define === 'function' && define.amd ? define(['exports'], factory) :
// UMD支持CJS, ESM, AMD, 所以这里剩下的只有ESM
// 如果现代浏览器支持globalThis,global就是globalThis
// 如果老版浏览器不执行globalThis,那么就是实际运行环境中的this
// 此时factory函数的参数exports就是global.util = {}
// 这里的util是指定的name,也就是说在浏览器中执行的时候,对应的函数和变量会被挂载到全局对象util中
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.util = {}));
}
// 1. this就是运行环境的globalThis
// 2. 回调函数
baz(this, (function (exports) {
'use strict';
function foo() {
console.log('foo');
}
exports.foo = foo;
}));
配置文件
// rollup的配置文件 - rollup.config.js
// 一般位于项目根目录
// 虽然rollup主要编译的代码是ESM
// 但是rollup在构建的时候 是运行在node环境中
// 所以配置文件的编写格式是CJS
module.exports = {
// 入口文件
input: './src/index.js',
// 输出到
output: {
// 编译格式
format: 'umd',
// 全局对象的名称
name: 'util',
// 文件名
file: './dist/bundle.js'
}
}
# 运行配置文件
# 通过-c参数 告诉rollup去加载对应的配置文件
# -c 后边可以指定自定义的配置文件名称和路径
# 如果不指定 默认就是 项目根目录下的rollup.config.js
npx rollup -c <config_file_name>
// rollup的配置文件 - rollup.config.js
module.exports = {
input: './src/index.js',
// output的值也可以是数组, 以便于输出运行在不同环境下的多个构建文件
// 方便用户进行按需引入
output: [
{
format: 'umd',
name: 'util',
// 在文件名中加上umd以区分运行在不同环境中的文件
file: './dist/bundle.umd.js'
},
{
format: 'iife',
name: 'util',
file: './dist/bundle.iife.js'
},
{
format: 'amd',
file: './dist/bundle.amd.js'
},
{
format: 'cjs',
file: './dist/bundle.cjs.js'
}
]
}
插件
库相关
@rollup/plugin-commonjs
默认情况下,rollup并不会处理CJS代码,而我们所编写的库可能会使用到一些基于CJS的库,例如lodash
所以为了我们可以正确使用CJS编写的库,就需要一个插件@rollup/plugin-commonjs
const commonjs = require('@rollup/plugin-commonjs')
module.exports = {
input: './src/index.js',
output: {
format: 'umd',
name: 'util',
file: './dist/bundle.js'
},
// 配置插件
plugins: [
// rollup中的每一个插件本质上都是一个个的函数
commonjs()
]
}
@rollup/plugin-node-resolve
ESM中对于模块路径的解析规则和CJS中对于模块路径的解析规则是不同的
所以为了我们可以正确解析CJS编写的模块,我们还需要使用另一个插件@rollup/plugin-node-resolve
const commonjs = require('@rollup/plugin-commonjs')
const nodeResolve = require('@rollup/plugin-node-resolve')
module.exports = {
input: './src/index.js',
output: {
format: 'umd',
name: 'util',
file: './dist/bundle.js'
},
plugins: [
commonjs(),
nodeResolve()
]
}
此时库中一些基于CJS编写的库就可以被构建到最终输出的代码中
但是
-
这会导致构建后的输出文件越来越大
-
如果使用库的用户已经安装了这些基于CJS编写的库,就存在重复安装对应库的问题
如库依赖lodash,如果使用者的项目已经安装了loadsh
难么lodash就在库和项目中被重复安装
所以在实际构建库的时候,这些第三方依赖并不会被打包到最终输出的代码中
而是会被作为对等依赖,要求使用者在使用这个库之前必须安装对应的依赖
// rollup.config.js
const commonjs = require('@rollup/plugin-commonjs')
const nodeResolve = require('@rollup/plugin-node-resolve')
module.exports = {
input: './src/index.js',
output: {
format: 'umd',
name: 'util',
file: './dist/bundle.js',
// globals 属性是用来告诉 Rollup 打包过程中哪些模块是外部依赖,以及它们在全局命名空间中的名称
globals: {
'lodash': '_'
}
},
// 在构建的时候,不构建lodash
external: ['lodash'],
plugins: [
commonjs(),
nodeResolve()
]
}
// package.json
"peerDependencies": {
"lodash": "^4.17.21"
}
@rollup/plugin-babel
在我们编写库的时候,依旧可以需要使用ES6+语法或TypeScript语法
所以我们依旧需要将这些代码通过babel等工具进行转换
转换为浏览器可以直接识别和运行的代码
// rollup.config.js
const commonjs = require('@rollup/plugin-commonjs')
const nodeResolve = require('@rollup/plugin-node-resolve')
const babel = require('@rollup/plugin-babel')
module.exports = {
input: './src/index.js',
output: {
format: 'umd',
name: 'util',
file: './dist/bundle.js',
globals: {
'lodash': '_'
}
},
external: ['lodash'],
plugins: [
commonjs(),
nodeResolve(),
babel({
// bundled是默认值,但是推荐明确显示定义
babelHelpers: 'bundled',
exclude: /node_modules/
})
]
}
// babel.config.js
module.exports = {
presets: [
'@babel/preset-env'
]
}
@rollup/plugin-terser
构建后的代码需要进行丑化和压缩
const commonjs = require('@rollup/plugin-commonjs')
const nodeResolve = require('@rollup/plugin-node-resolve')
const babel = require('@rollup/plugin-babel')
const terser = require('@rollup/plugin-terser')
module.exports = {
input: './src/index.js',
output: {
format: 'umd',
name: 'util',
file: './dist/bundle.js',
globals: {
'lodash': '_'
}
},
external: ['lodash'],
plugins: [
commonjs(),
nodeResolve(),
// @rollup/plugin-terser 拥有默认的terser配置,可以不手动指定
terser(),
babel({
babelHelpers: 'bundled',
exclude: /node_modules/
})
]
}
业务相关
@rollup/plugin-html
// 功能类似于html-webpack-plugin
const html = require('@rollup/plugin-html');
module.exports = {
// ....
plugins: [
// ....
]
}
rollup-plugin-delete
// 功能类似于clean-webpack-plugin
const clean = require('rollup-plugin-delete')
module.exports = {
// ....
plugins: [
// ....
clean({
// 指定需要删除的文件夹
targets: 'dist/*'
})
]
}
rollup-plugin-postcss
module.exports = {
// ....
plugins: [
// ....
]
}
rollup-plugin-vue
module.exports = {
// ....
plugins: [
// ....
]
}
@rollup/plugin-replace
module.exports = {
// ....
plugins: [
// ....
]
}
rollup-plugin-serve
module.exports = {
// ....
plugins: [
// ....
]
}
rollup-plugin-livereload
module.exports = {
// ....
plugins: [
// ....
]
}
区分开发环境
babelHelpers其余各选项的含义
export.xxx 错误