Skip to content

Webpack 和 Rollup:相同但不同

致读者

本译文在忠于原文的基础上,融入了译者基于自身领域知识所添加的解释性内容和本地化表达。这些精心添加的内容旨在增进读者对原文核心信息的理解。如对内容有任何疑问,欢迎您参与评论区讨论,或查阅原文以作参考。

本周,Facebook 将一个重要的 pull request 合并到 react 中,用基于 rollup 的构建流程取代了现有的构建系统。这引发了许多人提问:“为什么选择 rollup 而不是 webpack?”

这个问题很合理。webpack 是现代 javascript 社区最成功的项目之一,每月有数百万次下载量,为数万个网站和应用程序提供支持。它拥有庞大的生态系统、众多贡献者,而且——这在社区开源项目中很少见——获得了实质性的财务支持

相比之下,rollup 就像一条小鱼。但 react 并不是唯一选择 rollup 的项目——vueemberpreactd3three.jsmoment 以及其他许多知名库也都在使用 rollup。那么这是为什么呢?为什么我们不能只有一个所有人都认可的 javascript 模块打包工具呢?

两个打包工具的故事

webpackTobias Koppers2012 年创建,目标是在解决现有工具无法解决的难题:构建复杂的单页应用程序(SPA)。特别是以下两个特性彻底改变了这一切:

  1. 代码分割

    使得将应用拆分成可 按需加载 的可管理块成为可能。这意味着用户可以不用等待整个应用下载和解析,就可以获得交互式网站体验。你可以手动完成这项工作,实现起来会有些复杂度。

  2. 静态资源处理

    图片和 css 等静态资源可以被导入到应用中,并被视为模块依赖图中的一个特殊的节点。开发者不需要再小心翼翼地将文件放在正确的文件夹中,也不需要使用特殊的脚本来为文件 URL 添加哈希值,因为这些任务 webpack 可以为你很好的处理。

rollup 项目创建目的则与之不同:它的核心目的是尽可能高效地构建 javascript 库的平面分发(flat distributables)版本,实现上充分利用 ES2015 模块的巧妙设计。其他模块打包工具(例如 webpack 等)的工作方式是将每个模块包装在一个函数中,将它们放在一个包含浏览器友好的 require 实现的包中,然后逐个解析它们。如果你的应用需要 按需加载 等功能,这样做是值得的,但若不需要那么这有点浪费。而且如果你有很多模块,情况会变得更糟。

ES2015 模块启用了一种不同解析方式,也就是 rollup 所实现的方式。所有代码都放在同一个作用域中并一次性求值,从而产生更精简、更简单的代码、启动更快。你可以在 rollupREPL 中自己体验这一点。

rollup 处理方式虽好但这是有代价的,代码分割 实现起来更为棘手。在撰写本文时 rollup 还不支持这个功能。同样,rollup 也不支持热模块替换(HMR)特性。

对于使用 rollup 的人来说,可能最大的痛点是 —— 虽然 rollup 可以处理大多数 commonjs 模块(通过 @rollup/plugin-commonjs 插件),但有些东西就是无法将 commonjs 转换为 ES2015(主要原因是 commonjs 的高度动态性和原生支持 非严格模式,而 ES2015 模块只能在严格模式下加载),但 webpack 则可以从容地处理你扔给它的任何模块。

我应该使用哪个?

到这里,希望已经清楚为什么这两个工具可以共存并相互支持,因为他们服务于不同的目的,简而言之:

使用 webpack 构建应用程序,使用 rollup 构建库

这并不是一个硬性规定,许多网站和应用程序都是用 rollup 构建的,许多库也是用 webpack 构建的。但这是一个很好的经验法则。

如果你需要代码分割,或者有很多静态资源,或者正在构建具有大量 commonjs 依赖的项目,webpack 是更好的选择。但如果你的代码库是通过 ES2015 模块编写的,而且你正在制作供其他人使用的东西,你可能更需要 rollup

包作者:使用 pkg.module

长期以来,使用 javascript 库有点像碰运气。因为你和库作者实际上必须就模块系统达成一致。如果你使用 browserify 而库作者更喜欢 amd,你就必须在实际构建任何东西之前将这两种模块系统粘在一起(类似在 esm 中加载 commonjs 模块时,rollup 需要将 commonjs 转换为 esm)。通用模块定义(umd)格式在某种程度上解决了这个问题,但因为它没有在任何地方强制执行,你永远不知道会得到什么。

ES2015 改变了这一切,因为 importexport 是语言的一部分。在未来,这个规范将不会有歧义,事情会运作得更加顺畅。不幸的是,由于浏览器(大多数)和 node 还不支持 importexport 语义,我们仍然需要提供 umd 的产物(如果你构建的产物是仅用于 node 执行,则需要 commonjs 产物)。

通过在库的 package.json 文件中添加 "module": "dist/my-library.es.js" 字段(又称 pkg.module),现在就可以同时提供 umdES2015。这很重要,因为 webpackrollup 都可以使用 pkg.module 来生成最高效的代码——在某些情况下,他们甚至都可以将库中未使用的代码剔除掉(tree-shaking)。

rollupwiki 上了解更多关于 pkg.module 的信息。

根据 CC BY-SA 4.0 许可证发布。 (95ee605)