打包工具 rollup.js 入门教程:从两个 ES 模块到可分发产物

原作者:阮一峰。原文发表于 2022 年 5 月 5 日,见《打包工具 rollup.js 入门教程》。本文在完整核对原文后整理,保留其示例与学习路径;标为“编者注”的内容是本次补充。

版本说明:这是一篇 2022 年的入门教程。文中关于工具选型、性能与配置偏好的说法应放回当时语境理解。以下命令与输出仅做静态核对,本次没有安装 Rollup、执行打包或做体积基准测试。

main.js 只导入 addPi;Rollup 分析 add.js 后保留 PI 与 addPi,移除本例未使用且无副作用的 E 与 addE,生成 bundle.js。
从依赖分析到产物的结构示意。原创技术配图,非运行截图。

一、Rollup 解决什么问题

Rollup 是 JavaScript 模块打包工具,可以从入口文件出发,把依赖组织为浏览器或其他运行环境使用的产物。原文用“把多个脚本合并为一个脚本”解释打包,主要列出三个背景:早期浏览器不支持模块;Node.js 的模块机制需要转换才能适配浏览器;合并脚本曾是减少加载开销的常见手段。

编者注:现代浏览器原生支持 ES 模块,实际产物也可能拆分成多个 chunk。一个大文件是否比多个小文件快,取决于协议、缓存、并行加载与按需加载策略,不能把原文这条简化判断当作通用性能保证。

原作者认为,Rollup 起初专注 ES 模块,简单场景可以不写配置直接打包;后来也能处理 CommonJS,但增加相应配置后,简单性优势会减弱。因此原文建议优先把它用于 ES 模块,并把对 Webpack 难易程度的比较作为个人选型意见。这些观点不是本次对当前工具生态的测评。还不清楚两种模块机制的读者,可先阅读原文引用的ES6 模块教程。

二、安装与查看帮助

原文采用全局安装,前提是已经具备 Node.js 和 npm:

npm install --global rollup
rollup --help

也可以把后续命令中的 rollup 换成 npx rollup。例如:

npx rollup --help

编者注:npx 在本地没有相应包时可能下载并运行包,因此“不预先安装”不等于“不发生安装或执行”。全局安装会改动全局工具环境,未指定版本也不利于复现。实际项目宜按项目要求固定开发依赖与锁文件,使用可信软件源;不要为了教程随意提升权限。参见原文引用的npx 使用教程。

三、用两个文件观察打包

先准备库文件 add.js,导出两个函数:

// add.js
const PI = 3.14;
const E = 2.718;

export function addPi(x) {
  return x + PI;
}

export function addE(x) {
  return x + E;
}

再准备入口 main.js。它只导入 addPi():

// main.js
import { addPi } from './add.js';

console.log(addPi(10));

在这两个文件所在目录中,以入口文件为参数:

rollup main.js

Rollup 会沿入口的导入关系找到依赖。这个命令默认把生成代码写到标准输出。原文展示的产物如下,属于源文示例输出,并非本次执行结果:

const PI = 3.14;

function addPi(x) {
  return x + PI;
}

console.log(addPi(10));

本例内部的 import、export 已被展开后的代码代替;没有被入口使用的 addE() 和常量 E 不进入产物。这展示了 tree-shaking,常译为“摇树优化”:分析模块使用关系,删除能够安全判定为不需要的代码。

编者注:这里能够移除,是因为例子足够简单且相关代码没有必须保留的副作用。不能据此推断任何“看似没用”的代码都会被删除。原文所说“产物更小”也没有附跨工具基准数据,本文不把它作为普遍比较结论。

要把产物写入文件,增加 --file:

rollup main.js --file bundle.js

输出位置是 bundle.js。已有同名文件可能被覆盖,练习时应使用独立目录并核对路径。

四、多入口、输出格式与配置文件

多入口使用输出目录

如果有 m1.js、m2.js 两个入口,依次列出,并用 --dir 指定目录:

rollup m1.js m2.js --dir dist

原例会在 dist 中生成两个入口对应的文件;若两者存在可提取的共同依赖,还可能生成共享 chunk。具体共享文件名和拆分结果受版本及配置影响。

生成立即执行函数

--format iife 将本例产物包装成立即执行函数,适合以普通脚本方式加载的场景:

rollup main.js --format iife

本例入口只是打印结果,不向外导出库 API。如果改为有导出值的库,还需按输出格式要求考虑全局名称与外部依赖映射,不能直接把这一行当作所有库的完整配置。

紧凑输出和代码最小化

原文给出 --compact:

rollup main.js --compact

编者更正:按当前官方output.compact 说明,此项控制 Rollup 生成的包装代码是否紧凑,并不是对用户代码做完整压缩或混淆。因此原文把它简称为“代码最小化”不够准确。

原文另一条路线是把标准输出交给专门工具:

rollup main.js | uglifyjs --output bundle.js

这条管道先打包,再由 UglifyJS 处理结果并写文件。它要求已有合适版本的 uglifyjs,还应检查该版本对输入语法的支持和所需压缩参数;仅出现这条命令并不意味着已获得理想压缩率。这里保留原例,不替读者自动安装或执行。

把参数写入配置文件

原文的 rollup.config.js 如下:

// rollup.config.js
export default {
  input: 'main.js',
  output: {
    file: 'bundle.js',
    format: 'es'
  }
};

使用 -c 读取配置:

rollup -c

编者注:这是一份 ES 模块配置。当前 Node.js/Rollup 的加载规则与文件扩展名、项目 package.json 的 type 有关。若项目不是 ESM,可以采用 rollup.config.mjs 并明确执行 rollup -c rollup.config.mjs,参见官方配置文件说明。配置文件本身是可执行代码,不能不经审阅就加载陌生仓库配置或插件。

原作者不推荐为这个小例子额外引入配置文件,认为命令行已经足够清楚。这是针对简单场景的偏好;需要插件、多种产物或复用构建参数时,是否使用配置文件应按项目需要判断。

五、把 ES 模块转换为 CommonJS

这次以库文件 add.js 为入口,将其导出保留为 CommonJS API:

rollup add.js --format cjs

原文给出的转换结果是:

'use strict';

Object.defineProperty(exports, '__esModule', { value: true });

const PI = 3.14;
const E = 2.718;

function addPi(x) {
  return x + PI;
}

function addE(x) {
  return x + E;
}

exports.addE = addE;
exports.addPi = addPi;

与前面的 main.js 入口不同,这次 addPi 和 addE 都是库的公开导出,所以示例保留二者。不同版本对 __esModule 标记和格式细节的输出可能不同,关键是理解模块接口从 export 转为 exports 的过程。

来源、许可与审核范围

原文作者与版权归属:阮一峰,阮一峰的网络日志。原站声明为“自由转载—非商用—非衍生—保持署名”,链接至 CC BY-NC-ND 3.0。本次整理及配图经单独授权;保留原站许可声明,不据此为其他读者扩大再利用权限。本文增加版本、命令与安全注释,对原文过度简化的判断作了明示更正。

代码仅做静态审阅:示例未含硬编码秘密或外部输入拼接,但安装、配置加载、插件运行与文件输出仍有相应风险。本次没有执行示例,也没有验证所有 Rollup 版本或依赖组合;未发现问题不等于没有漏洞。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容