Skip to content
字数
2567 字
阅读时间
11 分钟

Rollup 和 Webpack 都是前端构建工具,它们的主要区别如下:

  1. 打包方式:Rollup 是采用 ES Module 进行打包,只会打包代码中用到的部分,支持 Tree Shaking,能够生成更小的打包文件,而 Webpack 采用的是 CommonJS 的方式进行打包,无法做到像 Rollup 一样的精简打包。

  2. 插件生态:Webpack 的插件生态更加丰富,支持更多的功能,如代码分割、热更新、文件压缩等等,而 Rollup 的插件相对较少。

  3. 使用场景:Rollup 适用于构建类库或组件库,Webpack 适用于构建复杂的应用程序。

  4. 性能:由于 Rollup 打包出来的文件更小,因此在加载和解析代码的时候更快,但在处理大型应用时可能不如 Webpack 快

Webpack 和 Rollup 作为前端端主流的模块打包工具,核心目标都是将分散的模块(如 JS、CSS 等)整合为可运行的产物,但两者的底层设计理念和实现原理存在显著差异,这些差异直接决定了它们的适用场景。以下从核心原理、模块处理、依赖解析、产物优化等维度对比两者的底层差异:

一、核心设计理念差异

这是两者所有差异的根源:

  • Webpack:定位为「前端应用打包工具」,设计初衷是处理复杂应用的依赖管理(尤其是浏览器环境),支持多种类型资源(JS、CSS、图片等),强调「万物皆模块」和「灵活性」,可通过插件扩展几乎所有功能。
  • Rollup:定位为「JavaScript 模块打包工具」,专注于构建库(Library)或简洁应用,核心目标是生成「精简、高效的代码」,强调「原生 ES 模块(ESM)的 tree-shaking 能力」和「输出代码的可读性」。

二、模块系统与依赖解析

1. 模块格式支持

  • Webpack

    • 底层支持多种模块格式:ES Modules(import/export)、CommonJS(require/module.exports)、AMD、UMD 等,甚至可以通过 loader 处理非 JS 模块(如 CSS、图片等)。
    • 原理:内部实现了一套「模块适配器」,将不同格式的模块统一转换为 Webpack 自有模块格式(类似 CJS 的 wrapper),例如对 ESM 的 import 会转换为 __webpack_require__ 方法调用,对 CJS 的 require 也会包装为同名方法,从而实现跨模块格式的依赖管理。
  • Rollup

    • 原生优先支持 ES Modules(ESM),对 CommonJS 的支持需要通过插件(如 @rollup/plugin-commonjs)转换,且转换过程相对复杂(因 CJS 是动态的,与 ESM 的静态分析特性冲突)。
    • 原理:依赖 ESM 的「静态结构」(导入导出在编译时可确定),通过遍历 ESM 的 import 语句构建依赖树,不支持非 JS 模块(需插件扩展,但并非核心能力)。

2. 依赖解析方式

  • Webpack

    • 采用「递归依赖解析 + 缓存」:从入口文件开始,递归解析所有 require/import 语句,将每个模块转换为「模块对象」(包含 id、依赖列表、代码等),并缓存已解析的模块避免重复处理。
    • 支持「动态依赖」:例如 require('./' + filename) 这类动态路径,Webpack 会通过正则匹配可能的文件路径,在打包时将所有可能的模块都包含进来(称为「context module」),灵活性高但可能导致冗余。
  • Rollup

    • 采用「静态依赖分析」:基于 ESM 的静态导入语句(import './a.js'),在编译时即可确定所有依赖路径,构建出清晰的依赖树(无动态依赖支持,需手动配置 external 或插件处理)。
    • 解析过程更简洁:因 ESM 静态特性,无需处理动态路径,依赖树构建效率更高,也为 tree-shaking 奠定了基础。

三、代码打包与输出逻辑

1. 模块包装方式

  • Webpack

    • 所有模块被包裹在 __webpack_modules__ 对象中,每个模块对应一个函数(参数为 moduleexports__webpack_require__ 等),通过 __webpack_require__(moduleId) 实现模块加载。
    • 示例(简化):
      javascript
      // 模块注册表
      const __webpack_modules__ = {
        './src/a.js': (module, exports, __webpack_require__) => {
          module.exports = 'a';
        },
        './src/index.js': (module, exports, __webpack_require__) => {
          const a = __webpack_require__('./src/a.js');
          console.log(a);
        }
      };
      // 加载入口模块
      __webpack_require__('./src/index.js');
    • 特点:通过运行时(runtime)代码管理模块加载,支持代码分割(chunk)、懒加载等动态特性,但会引入额外的运行时代码(增加产物体积)。
  • Rollup

    • 采用「扁平化打包」:将所有模块的代码合并到一个文件中,通过变量提升(hoisting)避免作用域冲突,直接执行代码逻辑,无额外运行时
    • 示例(简化):
      javascript
      // a.js 的代码
      const a = 'a';
      // index.js 的代码
      console.log(a);
    • 特点:输出代码接近手写逻辑,可读性高、体积小,但不支持动态加载(需通过插件模拟,如 @rollup/plugin-dynamic-import-vars)。

2. 代码分割(Code Splitting)

  • Webpack

    • 核心特性之一,支持多入口、动态 import()splitChunks 配置等多种代码分割方式,可生成多个 chunk 文件。
    • 原理:通过「chunk 图」管理模块与 chunk 的关系,每个 chunk 包含独立的模块集合和运行时代码,chunk 间通过 jsonpscript 标签加载(浏览器环境),支持复杂的依赖拆分逻辑。
  • Rollup

    • 早期不支持代码分割,后来通过 ESM 的动态 import() 支持,但功能较简单,主要用于生成多个输出文件(如按入口分割)。
    • 原理:基于 ESM 的静态分析,将动态 import() 对应的模块拆分为单独的 chunk,依赖浏览器原生 ESM 加载能力(或通过插件转换为 CJS 兼容格式),不支持 splitChunks 这类复杂的公共代码提取(需手动配置 manualChunks)。

四、优化策略实现

1. Tree Shaking(移除未使用代码)

  • Webpack

    • 依赖 ESM 的静态结构,在 mode: 'production' 下通过 terser-webpack-plugin 实现,需要结合 package.jsonsideEffects 标记副作用文件。
    • 原理:先标记所有未被引用的导出(unused harmony export),再通过压缩工具(Terser)删除这些代码。但因 Webpack 支持多种模块格式,Tree Shaking 效果受模块格式影响(CJS 无法被 Tree Shaking)。
  • Rollup

    • Tree Shaking 是核心特性,原生支持且效果更彻底,同样基于 ESM 的静态分析。
    • 原理:在依赖解析阶段即可识别未被使用的导出(如 export const unused = 1 且无引用),直接在打包时剔除这些代码,无需依赖压缩工具。因专注 ESM,无 CJS 转换的干扰,Tree Shaking 更精准。

2. 产物优化

  • Webpack

    • 侧重「应用级优化」,如代码分割、缓存策略(contenthash)、压缩(JS/CSS/图片)等,通过插件生态实现多样化优化。
    • 产物中包含运行时代码和 chunk 管理逻辑,适合浏览器环境的复杂应用,但体积相对较大。
  • Rollup

    • 侧重「库级优化」,输出代码简洁、无冗余,支持多种模块格式输出(ESM、CJS、UMD 等),适合作为库的打包工具。
    • 产物体积更小,可读性更高,但缺乏 Webpack 对多资源类型和复杂场景的支持。

五、插件系统设计

  • Webpack

    • 插件系统基于「事件流」(Tapable 库),通过钩子(hook)在构建的各个阶段(如 compilemakeemit 等)插入逻辑,几乎可以干预打包的每一步。
    • 灵活性极高,可实现 loader 处理非 JS 资源、自定义 chunk 分割、注入环境变量等复杂功能。
  • Rollup

    • 插件系统基于「钩子函数」,围绕模块解析、转换、生成等阶段设计(如 resolveIdtransformgenerateBundle 等),更专注于 JS 模块的处理。
    • 设计更简洁,插件逻辑通常与 ESM 处理强相关,扩展非 JS 资源时不如 Webpack 自然。

总结:底层差异的核心对比

维度WebpackRollup
核心目标复杂应用打包(多资源、动态加载)库或简洁应用打包(精简代码)
模块支持多格式(ESM/CJS/AMD 等)+ 非 JS 资源原生 ESM,CJS 需插件转换
依赖解析支持动态依赖,递归解析 + 缓存静态依赖分析,仅支持 ESM 静态导入
代码包装运行时管理(__webpack_require__扁平化合并,无运行时
代码分割强大,支持多策略(动态导入、公共 chunk)基础支持,依赖 ESM 动态导入
Tree Shaking依赖压缩工具,效果受模块格式影响原生支持,基于 ESM 静态分析,效果更优
产物特点体积较大,包含运行时体积小,可读性高,接近原生代码
插件生态庞大,覆盖全场景精简,专注 JS 模块处理

简言之,Webpack 是「全能型选手」,适合处理复杂应用的依赖和资源管理;Rollup 是「专精型选手」,适合生成精简、高效的库或简单应用代码。两者的底层差异本质上是「灵活性与精简性」「多场景支持与单一目标优化」的取舍。

参考

rollup打包产物解析及原理(对比webpack) - 掘金

贡献者

The avatar of contributor named as jiechen jiechen

页面历史

撰写