Rollup

7/15/2021 rollup

# 简介

  • Rollup 仅仅是一款 ESM 打包工具
  • Rollup 不支持类似 HMR 这类高级特性
  • Rollup 产生的初衷不是为了和 webpack 全面竞争;而是要提供充分利用 ESM 的特性的高效打包器,构建出结构比较扁平、性能比较出众的类库。

# 使用

  1. 我们准备三个文件,其中一个默认导出一个对象(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);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
  1. 然后安装 rollup,安装后node_modules/bin目录中会出现 rollup 的 cli,我们可以通过yarn rollup命令使用 rollup 进行打包;直接使用yarn rollup命令(不指定打包文件)会出现 rollup 帮助信息,需要通过rollup [options] <entry file>来使用 rollup

  2. 我们使用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);
})();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
  1. 可以看到打包后的内容非常干净,没有类似 webpack 打包后的辅助函数和引导定义的模块变量,没有多余的代码,同时默认开启了tree shaking优化打包结果,同时tree shaking最早也是在 rollup 的一个工具中被提出

# 配置文件

rollup 也支持配置文件的形式配置打包参数

  1. 在根目录新建rollup.config.js文件,默认导出一个对象,input 属性表示入口文件,output 表示出口属性(对象),该对象的属性对应打包的配置
// rollup.config.js
export default {
  input: "./src/index.js",
  output: {
    file: "dist/bundle.js",
    format: "iife",
  },
};
1
2
3
4
5
6
7
8
  1. 执行yarn rollup --config命令,rollup 才会读取配置文件,如果不执行--configrollup 默认不使用配置文件

  2. 可以在--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函数
  ],
};
1
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);
1
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);
})();
1
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(), // 使用这个插件就可以支持通过模块名导入第三方模块
  ],
};
1
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);
});
1
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);
1
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);
})();
1
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 会将代码放在一个函数中,无法拆分,所以需要使用amdcmd或者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");
});
1
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"
  }
}
1
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>
1

# rollup 选用原则

rollup 优势

  • 输出结果更加扁平(执行效率更高)
  • 自动抹除未引用代码(tree-shaking)
  • 打包结果依然完全可读

rollup 缺点

  • 加载非 ESM 的第三方模块比较麻烦
  • 模块最终都被打包到一个函数中,无法实现 HMR
  • 浏览器环境中,代码拆分功能依赖 AMD 库

如果是开发一个应用程序,会引入大量第三方库,同事需要 HMR 这样的方式来提升开发体验,且应用过大需要分包,这些在 rollup 上都会有一些欠缺,但是如果正在开发一个 js 的框架或者类库,这些缺点就可以忽略(react 和 vue 这种库都是使用 rollup 打包)

总结:webpack 大而全 rollup 小而美

不过随着 webpack 的发展,已经渐渐抹平了 rollup 的优势

Last Updated: 1/21/2025, 10:16:53 AM