rollup 是什么?如何使用?

简介: 继 Webpack、Vite 等前端工具链系列的了解之后,又碰到了 Rollup,我之前对 Rollup 的了解仅停留在 Vite 打包时使用、组里的大佬使用 Rollup 写过一个静态官网页面,为了

WebpackVite 等前端工具链系列的了解之后,又碰到了 Rollup,我之前对 Rollup 的了解仅停留在 Vite 打包时使用、组里的大佬使用 Rollup 写过一个静态官网页面,为了更深入的学习,就通过这篇文章来学习 Rollup 以及 ta 的使用

概述

Rollup 是一个 JavaScript 模块打包工具,可以将多个小的代码片段编译为完整的库和应用。与传统的 CommonJS 和 AMD 这一类非标准化的解决方案不同,Rollup 使用的是 ES6 版本 Javascript 中的模块标准。新的 ES 模块可以让你自由、无缝地按需使用你最喜爱的库中那些有用的单个函数。这一特性在未来将随处可用,但 Rollup 让你现在就可以,想用就用。

Rollup 其实就是打包工具,和 Webpack 一样,毕竟我们现在都是用模块化的思路构建应用,而模块化则需要一个入口和出口的概念,那么打包工具就是用来干这个事的,将应用的模块集成到一个入口

与 Webpack 的区别

Webpack 作为打包工具链的大哥,出道早、地位高,采取的模块化方案也是基于早于 ES ModuleCommonJS,而我们的 Rollup 就不一样了,主打的就是一个 ES ModuleRollup 会基于 ES Module 给你的工程提供全方面的解决方案,包括但不限于,兼容 CommonJS 库、提供不同模块标准的输出、Tree-Shaking...

使用

您可以在线查看源代码

命令行

按照 rollup.js 的中文文档先简单来上一段

npm install -D rollup

假设你有一个下面目录结构的项目

├── package-lock.json
├── package.json
├── public
│   ├── bundle.js
│   └── index.html
└── src
    ├── index.js
    └── utils
        └── math.js
// index.js
import { square } from "./utils/math";

console.log(square(2));
// math.js
export function square ( x ) {
  return x * x;
}

index.js 是你的入口文件,math.js 是你的自定义模块,boudle.js 是你的输出文件(有没有这个不要紧,rollup 会帮你处理的!)

使用以下命令即可完成打包

npx rollup src/index.js --file public/bundle.js --format iife

你也可以在 package.json 里面配置 "script" 属性

{
  ...
  "scripts": {
    "build": "rollup src/index.js --file public/bundle.js --format iife"
  },
  ...
}

让我们看看 bundle.js 的庐山真面目

(function () {
    'use strict';

    function square ( x ) {
        return x * x;
    }

    console.log(square(2));

})();

这是一个 iife(Immediately Invoked Functions Expressions) 格式的输出文件,在浏览器解析至其对应代码时,会立即执行(所以叫立即调用函数表达式)

IIFE 就是最早的模块化,比如 JQuery,早期前端‘模块化’开发中通过将模块一股脑的引入来实现模块在浏览器的导入,通常如下

<script src="./jqeury1.js"></script>
<script src="./jqeury2.js"></script>
<script src="./jqeury3.js"></script>
...
<script src="./jqeuryn.js"></script>

除了 IIFE 这个最早的模块化标准之外,前端还有其它的模块化标准,如下(按照出现时间顺序排列)

  1. IIFE
  2. CJS(CommonJS)
  3. AMD
  4. CMD
  5. UMD
  6. System.js
  7. ESM(ES Module)

目前主流的还不是 rollup 主打的 ESM,也就是还没有完成大一统,仍是群雄割据的状态,所以 rollup--format 选项就是为了兼容其它模块化准备的,即使项目使用的是 ESM 编写,但仍然能够输出其它格式(rollup 它真的,我哭死),其 --format 选项文档如下

rollup [option] [type]
# Options
#   -f, --format <format>       Type of output (amd, cjs, es, iife, umd, system)
# Example
# rollup src/index.js --file public/bundle.js --format iife

配置文件

实际上 rollup 给出的项目级别的例子是下面这个

rollup -f iife --globals jquery:jQuery,lodash:_ \
  -i src/app.js -o build/app.js -m build/app.js.map

这实在是太长了,且难以读懂,所以 rollup 给出了自己的配置文件,用 JS 对象的方式去表示 rollup 的选项,通常它被命名为 rollup.config.js

在项目根目录下创建 rollup.config.js,并覆写如下

// rollup.config.js
module.exports = {
  input: "src/index.js",
  output: {
    file: "public/bundle.js",
    format: "iife",
  },
};

运行命令

npx rollup -c

就可以得到同样的 bondle.js

注意:rollup.config.js 不能直接使用 ESM,比如 export default ... 语法(虽然官方文档的示例是这么写的),而是应该使用 CommonJS,具体详情可以参考文档 Using Configuration Files - rollup.js doc

其它更加详细的 API 就不多介绍了,不然就和文档没啥区别了(想要更深入了解 rollup 的可以去官方文档,说实话不是很长)

除了基本的 API 之外,其实 rollup 还有一些比较出色的亮点和使用技巧,如下

Tree-Sharking

像早期的 Webpackbrowserify 是会全量打包的,无法做到只导入使用到的,这是因为 CommonJS 的缺陷,而 ESM 就很好的解决了这个问题,因为它是静态分析的(具体原因不细讲了,不然我下一篇写啥呢?)

那怎么做和怎么体现这个 Tree-Sharking 呢?看下面这个例子

先下载 browserify,早期 Webpack 打包(Webpack 4 以前,不含 Webpack 4)和 browserify 一致(但后来 Webpack 也加入了 Tree-Sharking

npm install -D browserify

修改 math.js,并使用 CommonJS 标准,如下

// math.js
module.exports.square = function (x) {
  return x * x;
};

module.exports.cube = function (x) {
  return x * x * x;
};

修改 index.js,并使用 CommonJS 标准,如下

// math.js
const { square } = require("./utils/math.js");

console.log(square(2));

执行打包命令

npx browserify src/index.js -o public/bundle.js

得到的 bundle.js 如下

// 格式化之后的 bundle.js
(function () {
 // browserify 加载模块代码
 // ...
})()(
  {
    1: [
      function (require, module, exports) {
        const { square } = require("./utils/math.js");

        console.log(square(2));
      },
      { "./utils/math.js": 2 },
    ],
    2: [
      function (require, module, exports) {
        module.exports.square = function (x) {
          return x * x;
        };

        module.exports.cube = function (x) {
          return x * x * x;
        };
      },
      {},
    ],
  },
  {},
  [1]
);

你会发现,没有使用到的 cube 也被打包进来了

但是基于 ESMRollup 就不会这样(前提是你的项目得使用 ESM

修改 math.js 如下

// math.js
export function square ( x ) {
    return x * x;
}

export function cube ( x ) {
    return x * x * x;
}

重新打包,得到的 bundle.js 如下

(function () {
    'use strict';

    function square ( x ) {
        return x * x;
    }

    console.log(square(2));

})();

只会将用到的 square 打包进来,非常的好用

导入 node_modules 模块

rollup 也有和 Webpack 同样的设计,即插件设计,用户可以利用 rollup 暴露出来的钩子函数调用 rollup 生命周期的各种 API,而 @rollup/plugin-node-resolve 就是基于这个设计的插件

@rollup/plugin-node-resolve 是作为导入 node_modules 里的模块设计,你可能疑惑为什么需要一个插件去完成这一步,因为伴随 IDE 的智能提示、智能补全,使用 ESMCJS 去引用模块似乎是一件稀松平常的事情,但其实没有这么简单

要说引用模块,这就不得不提到我们平时是怎么从 require(...)import...from... 里拿到 npm 包了,

假设上面两种模块引用如下

  1. require('specifier')
  2. import...from 'specifier'

假设这个模块是在 node_modules 中的,会以下面这种方式拿到模块

Node 环境执行的文件下,包含上述模块引用的,会先通过算法解析出模块在磁盘的路径(即 node_modules 目录下的路径),然后通过 Node.js 自己的 I/O 拿到模块的文件,解析其中的代码得到模块,并返回

上面这个只是解析算法的一部分,还有更加的复杂的情况需要考虑,require(...)解析参考import...from...解析参考

想深入了解的同学,可以查看我这篇文章

回到 @rollup/plugin-node-resolve 这个插件,由于 Node.jsESM(也就是上面说的 import...from...)的支持还在逐步跟进,并不是所有的 Node.js 版本都支持 ESM 的解析算法,但大多数是支持 CJS 的,所以对不同的版本兼容使用 Rollup 就需要 @rollup/plugin-node-resolve,以保证我们能够在所有版本下使用‘未来’的 ESM

避免产生错误

(!) Unresolved dependencies
https://rollupjs.org/guide/en/#warning-treating-module-as-external-dependency

使用

使用 @rollup/plugin-node-resolve 参考如下

# 安装
npm install -D @rollup/plugin-node-resolve

修改 rollup.cofig.js

const resolve = require("@rollup/plugin-node-resolve");

module.exports = {
  // ...
  plugins: [resolve()],
};

再安装一个具有 ESM 导出的 npm

# the-answer 会导出一个静态值 42
npm install the-answer

修改 index.js

// index.js
import answer from "the-answer";

console.log(answer);

执行打包

npx rollup -c

得到 bundle.js

(function () {
    'use strict';

    var index = 42;

    console.log(index);

})();

如果你没有 @rollup/plugin-node-resolve 就会得到上面的错误

转换 CommonJS 模块

转换 CommonJS 模块也是一个兼容性的功能,因为现在大多数模块和 npm 包是用的 CommonJS 模块标准写的,而 Rollup 是基于 ESM 打包,因此正确的打包就需要将 CJS to ESM,而 Rollup 提供的插件就是 @rollup/plugin-commonjs,还是使用一个例子去教学

叛逆版(不使用 @rollup/plugin-commonjs

使用模块 lodash

npm install lodash

修改 index.js

// index.js
import _ from "lodash";

console.log(_.concat([1, 2, 3], 4, [5]));

执行打包

npx rollup -c

然后不出意外的出意外了

src/index.js → public/bundle.js...
(!) "this" has been rewritten to "undefined"
https://rollupjs.org/guide/en/#error-this-is-undefined
node_modules/lodash/lodash.js
17207:     root._ = _;
17208:   }
17209: }.call(this));
              ^
[!] RollupError: "default" is not exported by "node_modules/lodash/lodash.js", imported by "src/index.js".
https://rollupjs.org/guide/en/#error-name-is-not-exported-by-module

一般都是报没有使用 ESM 语法的错误

虚心好学版(使用 @rollup/plugin-commonjs

# 安装
npm install -D @rollup/plugin-commonjs

修改 rollup.cofig.js

const resolve = require("@rollup/plugin-node-resolve");
const commonjs = require("@rollup/plugin-commonjs");

module.exports = {
  // ...
  plugins: [resolve(), commonjs()],
};

再次执行打包

npx rollup -c

得到 bundle.js

你没看错是最终的 bundle.js17216 行,全量打包 lodash,使用 CJS 的模块被 @rollup/plugin-commonjs 转换后是无法进行 Tree-Sharking

总结

我认为 Rollup 作用在于开发者能够使用未来的模块标准去写代码(ES Module 是未来的新标准,浏览器和 Node.js 都将逐步实现),同时也支持兼容导入旧的 CommonJS 库,也兼容不同模块标准的输出,就像 Babel 之于 ES6,开发者可以大胆的使用新的,设计良好的 API 而不用过分担忧新旧浏览器的兼容,不像 Android,代码总是有对于不同 Android 版本的兼容,同时采用新标准的代码也将提高代码的生命周期,不至于因为 JS 版本的迭代迅速过时并面临后期的重构的成本

不过国内的环境应该不需要考虑这些问题

参考资料

  1. rollup.js 中文文档
  2. rollup.js 英文文档
相关文章
|
DataWorks 数据管理 大数据
DataWorks操作报错合集之如何解决表字段类型从string改为datetime报错
DataWorks是阿里云提供的一站式大数据开发与治理平台,支持数据集成、数据开发、数据服务、数据质量管理、数据安全管理等全流程数据处理。在使用DataWorks过程中,可能会遇到各种操作报错。以下是一些常见的报错情况及其可能的原因和解决方法。
|
JavaScript 前端开发 C++
CommonJS和ES6模块规范有何区别
【8月更文挑战第21天】
417 8
|
安全 Java 网络安全
Java Socket编程教程:构建安全可靠的客户端-服务器通信
【6月更文挑战第21天】构建安全的Java Socket通信涉及SSL/TLS加密、异常处理和重连策略。示例中,`SecureServer`使用SSLServerSocketFactory创建加密连接,而`ReliableClient`展示异常捕获与自动重连。理解安全意识,如防数据截获和中间人攻击,是首要步骤。通过良好的编程实践,确保网络应用在复杂环境中稳定且安全。
308 0
|
运维 小程序 前端开发
小程序开发问题之在小程序中调用my.chooseImage接口让用户选择图片如何解决
小程序开发问题之在小程序中调用my.chooseImage接口让用户选择图片如何解决
|
前端开发 JavaScript UED
CSS Transition(过渡效果)详解
CSS Transition(过渡效果)详解
1429 1
|
SQL 分布式计算 DataWorks
DataWorks产品使用合集之如何将STRING类型转换为DATETIME类型
DataWorks作为一站式的数据开发与治理平台,提供了从数据采集、清洗、开发、调度、服务化、质量监控到安全管理的全套解决方案,帮助企业构建高效、规范、安全的大数据处理体系。以下是对DataWorks产品使用合集的概述,涵盖数据处理的各个环节。
265 1
|
存储 关系型数据库 MySQL
MySQL8 中文参考(二十五)(1)
MySQL8 中文参考(二十五)
125 0
|
存储 安全 Java
阿里云云效产品使用合集之怎么设置使用npm私有仓库进行流水线拉取依赖
云效作为一款全面覆盖研发全生命周期管理的云端效能平台,致力于帮助企业实现高效协同、敏捷研发和持续交付。本合集收集整理了用户在使用云效过程中遇到的常见问题,问题涉及项目创建与管理、需求规划与迭代、代码托管与版本控制、自动化测试、持续集成与发布等方面。
Element - ui :el-input 输入只能是数字并且小数点后只能是1-2位
Element - ui :el-input 输入只能是数字并且小数点后只能是1-2位
5441 1
|
JavaScript 前端开发 索引
LayUI前框框架普及版(二)
LayUI前框框架普及版
230 0