news 2026/9/15 19:24:25

基于 Rolldown 的 output.format 全解析:ES、CommonJS、IIFE 与 UMD 的选型与生成原理

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于 Rolldown 的 output.format 全解析:ES、CommonJS、IIFE 与 UMD 的选型与生成原理

基于 Rolldown 的 output.format 全解析:ES、CommonJS、IIFE 与 UMD 的选型与生成原理

【免费下载链接】rolldownFast Rust bundler for JavaScript/TypeScript with Rollup-compatible API.项目地址: https://gitcode.com/GitHub_Trending/ro/rolldown

Rolldown 是一款基于 Rust 实现、API 与 Rollup 兼容的 JavaScript/TypeScript 打包器。output.format是决定产物以何种模块规范呈现的核心配置项,直接关系到产物在浏览器、Node.js 与各类构建工具之间的可用性。本文以仓库中的 output-format.md 为骨架,结合 JS 侧类型定义与 Rust 侧渲染实现,系统讲解escjsiifeumd四种格式的生成形态、适用场景与配套选项,帮助你为每一个具体目标环境选出正确格式,并理解 Rolldown 在底层是如何把这些格式落地的。

一、output.format是什么

在 Rolldown 的配置中,format是 OutputOptions 上的一个可选属性,其类型定义为:

export type ModuleFormat = 'es' | 'cjs' | 'esm' | 'module' | 'commonjs' | 'iife' | 'umd';

注意其中包含多组别名(output-options.ts):

  • 'es''esm''module'是同一格式,均表示 ES Module;
  • 'cjs''commonjs'是同一格式,均表示 CommonJS Module;
  • 'iife'表示 Immediately Invoked Function Expression(立即执行函数表达式);
  • 'umd'表示 Universal Module Definition(通用模块定义)。

format的默认值是'es'。无论指定哪种别名,Rolldown 都会先将其归一化为内部格式InternalModuleFormat,即'es' | 'cjs' | 'iife' | 'umd'(见 normalized-output-options.ts)。

在 Rust 核心侧,格式被建模为OutputFormat枚举,四个变体EsmCjsIifeUmd与 JS 侧的InternalModuleFormat一一对应(见 output_format.rs)。该枚举提供了一系列辅助方法,在打包的不同阶段被反复查询:

  • is_esm()/is_esm_or_cjs():判断是否需要按 ESM(或 ESM/CJS)语义处理;
  • keep_esm_import_export_syntax():仅在es格式下保留原生import/export语法;
  • should_call_runtime_require():除cjsumdiife外,其余格式需要运行时require支持;
  • source_type():将格式映射为 oxc 的SourceType——es对应.mjscjs对应.cjsiife/umd则作为带 script 标志的 CJS 处理。

例如在模块最终化阶段(finalizer_context.rs),当import.meta无法被替换时,错误诊断信息中会带上options.format.as_str()来提示用户当前产物的格式。

二、ES Module(es):推荐的默认格式

生成形态

当设置output.format: 'es'时,产物会使用标准的export语法:

function exportedFunction() { /* ... */ } let exportedValue = '/* ... */'; export { exportedFunction, exportedValue };

ES 模块(ESM)是 JavaScript 的官方模块标准。加载方式取决于运行环境:

  • 浏览器:使用<script type="module">
  • Node.js:使用.mjs扩展名,或在package.json中声明"type": "module"

为什么推荐

ES modules 是绝大多数场景下的推荐格式,原因在于:

  1. 它们是 JavaScript 规范的一部分,在浏览器与 Node.js 中通用;
  2. 静态分析友好,能够支撑 tree-shaking 等优化(对应 Rust 侧keep_esm_import_export_syntax()返回true,产物保留原生语法,便于下游继续优化);
  3. 互操作性最好。

相关细节

在 output-options.ts 的 JSDoc 中明确说明'es''esm''module'三者等价。此外,Rolldown 在platform相关文档中强调其默认输出格式始终是'esm',不随平台切换(例如 esbuild 在 Node.js 下默认输出 CJS,而 Rolldown 不会这么做),详情见 platform.md。

三、CommonJS Module(cjs):面向传统 Node.js

生成形态

当设置output.format: 'cjs'时,产物会使用exports变量:

function exportedFunction() { /* ... */ } let exportedValue = '/* ... */'; exports.exportedFunction = exportedFunction; exports.exportedValue = exportedValue;

Live Binding 语义保留

CommonJS 是 Node.js 在 ES modules 出现之前原生支持的模块格式。原文档特别指出:入口点的 ES 模块导出会被转换为exports上的 getter,以保留 live binding 语义——即导出值可以被导出方后续修改,导入方读到的始终是最新值。

这与 Rust 侧reference_needed_symbols.rs中针对OutputFormat::Cjs的专门分支处理相互印证:非 ESM 格式需要生成额外的互操作代码来衔接import/export语义(见 reference_needed_symbols.rs)。

适用场景

  • 目标环境是不支持 ES modules 的旧版 Node.js;
  • 需要与期望 CommonJS 的老旧包集成。

相关配套选项

output.format: 'cjs'常与以下选项配合使用:

  • dynamicImportInCjs:默认true,保留外部动态导入为import(...);设为false时改写为require(...),以兼容不支持动态import()的旧版 Node.js(见 output-options.ts);
  • esModule:默认'if-default-prop',决定是否为非 ES 格式的产物添加__esModule: true标记,该标记表明导出对象是 ES 模块的 namespace,且默认导出对应.default属性(见 output-options.ts);
  • externalLiveBindings:默认true,为外部导入生成 live binding 支持代码;设为false可生成更小的产物,但要求外部模块的导出不会变化(见 output-options.ts)。

四、IIFE:面向单<script>标签的即插即用产物

生成形态

IIFE 即“立即执行函数表达式”。当设置output.format: 'iife'时,产物会被包裹在一个 IIFE 中,假设同时设置了output.name: 'MyLibrary'

var MyLibrary = (function () { function exportedFunction() { /* ... */ } let exportedValue = '/* ... */'; return { exportedFunction, exportedValue }; })();

解决的问题

当使用不带type="module"<script>标签时,代码在全局作用域执行,不同脚本之间的变量可能互相冲突。IIFE 通过创建私有函数作用域封装所有内部变量,只暴露一个全局变量(例如 jQuery、_、React 这类库的挂载方式),从根本上避免了全局污染。

适用场景

  • 需要“扔进任意页面就能跑”的 drop-in 脚本与小组件;
  • 希望只暴露一个干净全局变量的库(如分析脚本、可嵌入小部件)。

实现层面

在 Rust 渲染管线中,iifeumd格式在生成代码时需要对每个模块增加一层缩进(因为产物整体被包裹在函数体中),见 render_chunk_to_assets.rs:

let needs_extra_indent = matches!( self.options.format, rolldown_common::OutputFormat::Iife | rolldown_common::OutputFormat::Umd );

相关配套选项

  • name:指定umd/iife格式下承载导出的全局变量名(见 output-options.ts);
  • globals:为umd/iife格式下被标记为 external 的导入提供id: variableName映射。例如:
export default defineConfig({ external: ['jquery'], output: { format: 'iife', name: 'MyBundle', globals: { jquery: '$', }, }, });

对应输入import $ from 'jquery'会生成var MyBundle = (function ($) { /* ... */ })($);(见 output-options.ts);

  • extend:默认false。设为true时全局变量以global.name = global.name || {}形式定义(可叠加),否则以global.name = {}形式覆盖(见 output-options.ts)。

五、UMD:多环境通用的历史方案

生成形态

UMD(Universal Module Definition)是一种可跨多环境工作的模式:AMD(RequireJS)、CommonJS(Node.js)与浏览器全局变量。当设置output.format: 'umd'时,产物会被一段环境探测代码包裹,假设设置了output.name: 'MyLibrary'

(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.myBundle = {}))); })(this, function (exports) { function exportedFunction() { /* ... */ } let exportedValue = '/* ... */'; exports.exportedFunction = exportedFunction; exports.exportedValue = exportedValue; });

这段包裹逻辑依次探测:

  1. 是否处于 CommonJS 环境(typeof exports === 'object' && typeof module !== 'undefined');
  2. 是否处于 AMD 环境(typeof define === 'function' && define.amd);
  3. 否则回退到浏览器全局,将导出挂到global.myBundle上。

现状与建议

原文档明确指出:UMD 在 ES modules 广泛支持之前很流行,因为它让一份构建产物在所有环境都能工作。但在今天,UMD 已基本没有必要:

  • 所有现代浏览器与 Node.js 都支持 ES modules;
  • 打包器会自动处理模块互操作;
  • UMD 格式带来额外运行时开销(环境探测代码),且更难被静态分析。

对新项目,请直接使用es格式。这与 Rolldown 默认输出'esm'的设计是一致的。

六、格式选型速查与常见组合

目标环境推荐格式关键理由
现代浏览器(原生 ESM)es<script type="module">直接加载,tree-shaking 友好
Node.js 库(双格式发布)es+cjs分别发布 ESM/CJS 两套产物,覆盖全部 Node 版本
传统 Node.js(无 ESM 支持)cjsexports语法在老版本可直接require
<script>全局脚本iife私有作用域 + 单一全局变量
需要同时兼容 AMD/CJS/全局(旧场景)umd一份产物多处可用,但有运行时开销

一个同时面向现代与旧环境的典型配置:

export default defineConfig({ input: 'src/index.js', output: [ { format: 'es', dir: 'dist/es', entryFileNames: '[name].mjs' }, { format: 'cjs', dir: 'dist/cjs', exports: 'named' }, ], });

七、格式在打包流程中的位置

格式选择不是发生在“最后一刻”,而是贯穿 Rolldown 的链接(link)与生成(generate)阶段。从源码中可以看到格式对多个环节的影响:

  • 导出创建(create_exports_for_ecma_modules.rs):Esm格式与其他格式在导出处理上走不同分支;
  • 模块导出种类判定(determine_module_exports_kind.rs):es格式的判定逻辑与iife/umd不同;
  • 跨 chunk 链接计算(compute_cross_chunk_links.rs):cjsesm分别有各自的特殊处理;
  • 执行顺序包装(order_wrapping.rs):非esm格式下需要额外的包装逻辑;
  • 渲染缩进(render_chunk_to_assets.rs):iife/umd的整体包裹决定了模块代码需要额外缩进。

理解这一点有助于排查问题:例如,同一个模块图在切换format后,chunk 划分与代码形态都可能发生变化,这是格式语义差异的必然结果,而非 bug。

总结

output.format是 Rolldown 输出配置中最基础也最关键的选项之一。es是默认值与绝大多数场景的首选;cjs服务于旧版 Node.js 与 CJS 生态;iife适合单<script>全局脚本;umd作为历史方案在新项目中应避免。在 Rolldown 内部,格式被归一化为OutputFormat枚举,并在链接、生成、渲染的各个阶段驱动不同的代码生成策略——这正是理解产物形态差异的钥匙。

【免费下载链接】rolldownFast Rust bundler for JavaScript/TypeScript with Rollup-compatible API.项目地址: https://gitcode.com/GitHub_Trending/ro/rolldown

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/15 19:21:35

Claude Code+微信小程序TDD云开发实战指南

1. 项目概述&#xff1a;这不是“用AI写代码”&#xff0c;而是重构小程序开发工作流“Claude Code 开发微信小程序实战&#xff1a;6 天做完 6 个里程碑”——这个标题乍看像营销话术&#xff0c;但在我带过三轮小程序团队、亲手交付过27个上线项目后&#xff0c;它背后的真实…

作者头像 李华
网站建设 2026/9/15 19:19:52

时延抖动本质与实战治理:从网络卡顿到精准控制

1. 时延抖动不是“网络卡”&#xff0c;而是数据包在时间维度上的“醉汉走路”很多人一听到“网络卡”&#xff0c;第一反应是带宽不够、路由器太旧、WiFi信号弱——这些确实会影响网速&#xff0c;但它们主要拖慢的是平均传输速度。而“时延抖动”&#xff08;Jitter&#xff…

作者头像 李华