Rollup是一个面向 JavaScript 的模块打包工具(Module Bundler),它将分散在多个文件中的 ES Module 代码合并为一个或少数几个输出文件。Rollup 以其卓越的 Tree-shaking 能力著称——能够自动分析模块间的依赖关系,剔除未被使用的代码,从而显著减小最终的打包体积。Rollup 在 JavaScript 库的开发中被广泛采用,许多知名库(如 Vue、React 的部分工具链)都使用 Rollup 作为构建工具。Rollup 的核心设计哲学是“约定优于配置”,它默认遵循 ES Module 规范,减少了额外配置的需求。与 Webpack 等通用打包工具相比,Rollup 更专注于库的构建场景,输出代码更干净、更易于阅读和调试。
插件(Plugin)是 Rollup 扩展功能的核心机制。Rollup 本身只提供基础的打包能力,所有高级功能——如 TypeScript 编译、代码压缩、资源处理、环境变量替换等——都通过插件实现。一个 Rollup 插件是一个导出的 JavaScript 对象,必须包含 name 属性(字符串类型,用于标识插件)和一个或多个 Hook 处理函数。插件可以拦截 Rollup 构建过程中的各个阶段,在特定的 Hook 点注入自定义逻辑。Rollup 的插件系统设计借鉴了构建工具的管道模式(Pipeline),多个插件按顺序执行,形成一条处理链。这种设计使得插件之间的职责划分清晰,便于组合和复用。Rollup 官方维护了一系列核心插件(如 @rollup/plugin-node-resolve、@rollup/plugin-commonjs 等),社区也贡献了大量高质量的插件。
Hook(钩子)是 Rollup 插件系统的核心概念,代表了构建过程中的特定阶段或事件点。每个 Hook 都有明确的名称、参数和返回值规范,插件通过实现对应的 Hook 处理函数来参与构建流程。常见的 Hook 包括:resolveId(在模块标识符解析阶段被调用,用于自定义模块的查找路径)、load(在模块内容加载阶段被调用,用于自定义模块的读取方式)、transform(在模块代码转换阶段被调用,用于修改模块的源代码)、renderChunk(在代码块渲染阶段被调用,用于修改最终输出的代码块内容)、generateBundle(在构建产物生成前被调用,用于访问或修改整个输出 bundle)。Hook 可以是同步的或异步的,部分 Hook 支持返回 Promise 以处理异步操作。Hook 的执行顺序由 Rollup 内部的构建管线决定,开发者需要理解各 Hook 的触发时机才能正确使用。
AST(抽象语法树)是源代码的树状结构表示,每个节点对应代码中的一个语法元素(如变量声明、函数调用、表达式等)。在 Rollup 插件开发中,AST 是进行精确代码操作的核心工具。当简单的字符串替换无法满足需求时(例如需要准确识别变量声明而不误匹配注释中的同名文本),开发者需要将代码解析为 AST,遍历目标节点进行修改,然后将修改后的 AST 重新生成为代码字符串。Rollup 提供了 this.parse() 方法,允许插件在 transform Hook 中将模块代码解析为 AST。开发者通常配合 AST 遍历库(如 acorn、estree-walker 等)来遍历和修改 AST 节点。AST 操作虽然比正则替换更复杂,但能够提供精确的语义感知能力,是编写高质量代码转换插件的推荐方式。
Tree-shaking(摇树优化)是 Rollup 最具标志性的特性之一。该技术通过静态分析 ES Module 的导入导出关系,识别出代码中未被使用的导出(dead code),并在最终输出中将其剔除。Tree-shaking 的名称来源于“摇晃代码树,让枯死的叶子(未使用的代码)掉落”的形象比喻。Rollup 能够实现高效的 Tree-shaking 得益于 ES Module 的静态结构特性——import 和 export 语句在代码执行前就已经确定了模块间的依赖关系,这使得 Rollup 可以在编译阶段进行精确的依赖分析。相比之下,CommonJS 的 require() 是动态的,难以进行静态分析,因此 Tree-shaking 效果较差。开发者在编写供 Rollup 打包的库时,应当优先使用 ES Module 的导入导出语法,以确保 Tree-shaking 能够正常工作。
ES Module是 ECMAScript 2015(ES6)引入的官方模块系统规范。它使用 import 和 export 关键字来声明模块的依赖和导出,具有静态结构、异步加载、作用域隔离等特性。ES Module 是 Rollup 处理的核心输入和输出格式——Rollup 默认假设所有输入文件都是 ES Module,并将多个 ES Module 合并为一个优化后的 ES Module 输出。与 CommonJS(Node.js 传统模块系统)相比,ES Module 的静态结构使得 Tree-shaking 成为可能,因此 Rollup 社区强烈推荐使用 ES Module 格式。在插件开发中,transform Hook 接收的 code 参数和返回的代码字符串都应当是合法的 ES Module 语法。构建输出区域展示的也是 ES Module 格式的代码。
transform Hook是 Rollup 插件中最常用的 Hook 之一。它在每个模块的代码被加载后、被其他插件进一步处理前被调用,是修改模块源代码的主要切入点。transform Hook 的处理函数接收两个参数:code(模块的原始源代码字符串)和 id(模块的文件路径或标识符字符串)。处理函数需要返回转换后的代码字符串,或者返回一个包含 code(转换后的代码)和 map(Source Map 对象)的返回值。如果不需要修改代码,可以返回 null 或 undefined 以跳过处理。transform Hook 可以是同步的也可以是异步的——当需要进行异步操作(如读取额外文件、调用远程 API 等)时,可以返回 Promise。多个插件的 transform Hook 会按插件数组的顺序依次执行,前一个插件的输出会作为后一个插件的输入,形成代码转换的管道链。
resolveId Hook用于自定义模块标识符的解析过程。当 Rollup 遇到一个 import 语句时(如 import foo from './bar'),它需要将相对路径或模块名称解析为实际的文件路径。resolveId Hook 允许插件拦截这一解析过程,返回自定义的解析结果。处理函数接收两个参数:source(import 语句中的原始标识符字符串)和 importer(发起导入的模块路径)。如果插件决定处理该标识符,需要返回一个字符串(解析后的模块路径)或一个包含 id 和 external 属性的对象。如果插件不关心该标识符,返回 null 即可,Rollup 将继续使用默认的解析逻辑或后续插件的 resolveId。resolveId 与 load 的区别在于:resolveId 负责“找到模块在哪里”,而 load 负责“读取模块的内容”。两者配合工作,使得插件可以完全控制模块的发现和加载过程。
Source Map(源码映射)是一种将编译、压缩或转换后的代码映射回原始源代码的技术文件。在 Rollup 插件开发中,当插件对模块代码进行了转换操作时,理想的做法是同时生成对应的 Source Map,以便在调试时能够准确地定位到原始源代码的位置。Rollup 的 transform Hook 支持返回包含 code 和 map 的对象,其中 map 应当是一个符合 Source Map 规范的对象(通常通过库如 magic-string 生成)。如果插件不返回 Source Map,开发者在调试输出代码时将难以对应到原始的源代码行号,这会显著增加调试难度。在沙盒环境中,虽然调试需求相对简单,但养成生成 Source Map 的习惯对于开发生产级别的插件非常重要。
Rollup 配置文件(rollup.config.js)是 Rollup 项目的配置入口,用于定义输入文件、输出格式、插件列表、外部依赖等构建参数。在本地开发中,开发者需要创建并维护这个配置文件。典型的配置文件结构包括 input(入口文件路径)、output(输出配置,包括 format、file、banner 等字段)和 plugins(插件数组)。rollup.config.js 支持导出一个配置对象、一个配置数组(多入口构建)或一个返回配置的函数。沙盒工具简化了这一配置过程——开发者只需关注插件代码和源代码,配置文件由沙盒内部自动生成和管理。但理解配置文件的结构对于将沙盒中开发的插件迁移到本地项目中仍然非常重要。
构建管线(Build Pipeline)是 Rollup 内部处理构建任务的有序流程。整个管线包括模块解析(resolveId)到模块加载(load)到模块转换(transform)到模块作用域分析到代码树摇(tree-shaking)到代码块生成(renderChunk)到产物输出(generateBundle)等多个阶段。每个阶段对应一个或多个 Hook,插件通过实现这些 Hook 来介入构建流程的不同环节。理解构建管线对于编写正确的插件至关重要——开发者需要知道在哪个阶段插入自定义逻辑才能达到预期效果。例如,如果需要修改模块的导入路径,应当使用 resolveId Hook;如果需要修改模块的源代码,应当使用 transform Hook;如果需要修改最终输出的代码格式,应当使用 renderChunk Hook。沙盒的构建统计面板中展示的 Hook 调用链路信息,正是构建管线执行过程的直观体现。
UD5工具箱