# 简介
- Rollup 仅仅是一款 ESM 打包工具
- Rollup 不支持类似 HMR 这类高级特性
- Rollup 产生的初衷不是为了和 webpack 全面竞争;而是要提供充分利用 ESM 的特性的高效打包器,构建出结构比较扁平、性能比较出众的类库。
# 使用
- 我们准备三个文件,其中一个默认导出一个对象(message.js),一个导出两个函数(logger.js),使用最后使用 index.js 去调用其中的对象和函数
// message.js
export default {
hi: "Hey Guys, I'm wxy",
};
// logger.js
export const log = (msg) => {
console.info("------------ INFO --------------");
console.info(msg);
console.info("--------------------------------");
};
export const error = (msg) => {
console.info("------------ ERROR --------------");
console.info(msg);
console.info("--------------------------------");
};
// index.js
import { log } from "./logger";
import message from "./message";
const msg = message.hi;
log(msg);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
然后安装 rollup,安装后
node_modules/bin
目录中会出现 rollup 的 cli,我们可以通过yarn rollup
命令使用 rollup 进行打包;直接使用yarn rollup
命令(不指定打包文件)会出现 rollup 帮助信息,需要通过rollup [options] <entry file>
来使用 rollup我们使用
yarn rollup ./src/index --format iife
指定打包的入口文件和输出的格式(iife 就是自调用函数),此时会直接将打包后的结果输出到控制台,我们使用--file ./dist/bundle.js
指定输出到文件中
// 打包后的结果
(function () {
"use strict";
const log = (msg) => {
console.info("------------ INFO --------------");
console.info(msg);
console.info("--------------------------------");
};
var message = {
hi: "Hey Guys, I'm wxy",
};
const msg = message.hi;
log(msg);
})();
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
- 可以看到打包后的内容非常干净,没有类似 webpack 打包后的辅助函数和引导定义的模块变量,没有多余的代码,同时默认开启了
tree shaking
优化打包结果,同时tree shaking
最早也是在 rollup 的一个工具中被提出
# 配置文件
rollup 也支持配置文件的形式配置打包参数
- 在根目录新建
rollup.config.js
文件,默认导出一个对象,input 属性表示入口文件,output 表示出口属性(对象),该对象的属性对应打包的配置
// rollup.config.js
export default {
input: "./src/index.js",
output: {
file: "dist/bundle.js",
format: "iife",
},
};
2
3
4
5
6
7
8
执行
yarn rollup --config
命令,rollup 才会读取配置文件,如果不执行--config
rollup 默认不使用配置文件可以在
--config
指定配置文件,进行多种打包配置,类似开发和生产环境,yarn rollup --config rollup.config.js
# rollup 使用插件
rollup 自身自带的功能就是对 ESM 进行打包,如果想导入 CommonJS 模块或者编译 ECMAScript 的新特性,就只能使用插件进行扩展(插件是 rollup 唯一扩展途径),不想 webpack 有 loader、plugin 和 minimizer 的扩展方式
我们使用@rollup/plugin-json
作为示例,以前的 json 插件是rollup-plugin-json
不过已经废弃了。
// rollup.config.js
import json from "@rollup/plugin-json";
export default {
input: "./src/index.js",
output: {
file: "dist/bundle.js",
format: "iife",
},
plugins: [
// plugins接受一个数组
json(), // 传入json的调用结果而不是json函数
],
};
2
3
4
5
6
7
8
9
10
11
12
13
14
我们在 index 中通过 import 方式导入 package.json,json 文件中的每个属性都会作为单独的导出成员
tips
导入 package.json 时遇到一个报错,
Warning: To load an ES module, set "type": "module" in the package.json or use the .mjs extension.
package.json 作为 mjs 方式导出了,我们需要配置type: 'module'
转化为 ESM
// index.js
import { log } from "./logger";
import message from "./message";
import { name, version } from "../package.json";
const msg = message.hi;
log(msg);
log(name);
log(version);
2
3
4
5
6
7
8
9
10
11
运行yarn rollup --config
,发现 json 中name、version
属性被打包出来了,而其他属性没有被打包,应该是tree shaking
掉了
(function () {
"use strict";
const log = (msg) => {
console.info("------------ INFO --------------");
console.info(msg);
console.info("--------------------------------");
};
var message = {
hi: "Hey Guys, I'm wxy",
};
var name = "rollup-01";
var version = "1.0.0";
const msg = message.hi;
log(msg);
log(name);
log(version);
})();
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# rollup 加载 npm 模块
rollup 默认不支持使用模块名的方式导入 npm 模块,我们可以使用@rollup/plugin-node-resolve
插件来支持直接使用模块名导入
import json from "@rollup/plugin-json";
import resolve from "@rollup/plugin-node-resolve";
export default {
input: "src/index.js",
output: {
file: "dist/bundle.js",
format: "iife",
},
plugins: [
json(),
resolve(), // 使用这个插件就可以支持通过模块名导入第三方模块
],
};
2
3
4
5
6
7
8
9
10
11
12
13
14
我们安装lodash-es
(由于 rollup 默认不支持 cjs 方式,所以使用 ESM)模块作为示例
import { log } from "./logger";
import message from "./message";
import { name, version } from "../package.json";
import _ from "lodash-es";
const msg = message.hi;
log(msg);
log(name);
log(version);
_.map([1, 2, 3], (item) => {
log(item);
});
2
3
4
5
6
7
8
9
10
11
12
13
14
15
这样就会将 lodash 中的所有内容整体打包到 bundle.js 文件中
# 加载 CommonJS 模块
rollup 默认不支持 commonJS,但是目前大量 npm 包还是使用 commonJS,可以使用@rollup/plugin-commonjs
插件配置,就能使用 commonJS 了
// cjs-module.js
module.exports = {
foo: "bar",
};
// index.js
import cjs from "./cjs-module"; // 使用ESM方式导入
import { log } from "./logger";
log(cjs);
2
3
4
5
6
7
8
9
10
打包后的文件中有一个getDefaultExportFromCjs
函数,就是对 commonJS 的兼容
(function () {
"use strict";
const log = (msg) => {
console.info("------------ INFO --------------");
console.info(msg);
console.info("--------------------------------");
};
function getDefaultExportFromCjs(x) {
return x &&
x.__esModule &&
Object.prototype.hasOwnProperty.call(x, "default")
? x["default"]
: x;
}
var cjsModule = {
foo: "bar",
};
var cjs = /*@__PURE__*/ getDefaultExportFromCjs(cjsModule);
log(cjs);
})();
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# rollup 代码拆分
rollup 新版本中可以使用 ESM 中的动态导入方式实现代码拆分
但是代码拆分不能使用立即执行函数(iife)的 format,因为 iife 会将代码放在一个函数中,无法拆分,所以需要使用
amd
、cmd
或者CommonJs
标准,但是在浏览器中只能使用 AMD 标准输出多个文件不能使用
--file
指令,而需要使用dir
才能输出多个文件
// rollup.config.js
export default {
input: "src/index.js",
output: {
dir: "dist",
format: "amd",
},
plugins: [],
};
// index.js
import("./logger").then(({ log }) => {
log("hi, wxy");
});
2
3
4
5
6
7
8
9
10
11
12
13
打包后的文件就会是一个 index.js(入口文件),一个 logger.js
# rollup 多入口打包
rollup 同样可以配置多入口打包,对于不同入口当中公用的部分,会自动提取到单个文件中,配置方式有两种
// 1. 使用数组
export default {
input: ["src/index.js", "src/index2.js"]
}
// 2. 使用对象,类似webpack
export default {
input: {
foo: "src/index.js",
a: "src/index2.js"
}
}
2
3
4
5
6
7
8
9
10
11
由于多入口打包会拆分代码,就不能使用 iife 的 format,这里使用 amd
AMD 规范打包的文件,在浏览器中执行必须通过实现 AMD 标准的库去加载,此处使用require.js
,同时使用data-main
属性指定入口
<script src="./require.js" data-main="./index.js"></script>
# rollup 选用原则
rollup 优势
- 输出结果更加扁平(执行效率更高)
- 自动抹除未引用代码(tree-shaking)
- 打包结果依然完全可读
rollup 缺点
- 加载非 ESM 的第三方模块比较麻烦
- 模块最终都被打包到一个函数中,无法实现 HMR
- 浏览器环境中,代码拆分功能依赖 AMD 库
如果是开发一个应用程序,会引入大量第三方库,同事需要 HMR 这样的方式来提升开发体验,且应用过大需要分包,这些在 rollup 上都会有一些欠缺,但是如果正在开发一个 js 的框架或者类库,这些缺点就可以忽略(react 和 vue 这种库都是使用 rollup 打包)
总结:webpack 大而全 rollup 小而美
不过随着 webpack 的发展,已经渐渐抹平了 rollup 的优势