Webpack and Rollup: the same but different
Refer
Source
: Webpack and Rollup: the same but different - April 7, 2017
Author
: Rich Harris
Translator
: SenaoXi
A Note to Our Readers
While maintaining fidelity to the source material, this translation incorporates explanatory content and localized expressions, informed by the translator's domain expertise. These thoughtful additions aim to enhance readers' comprehension of the original text's core messages. For any inquiries about the content, we welcome you to engage in the discussion section or consult the source text for reference.
本周,Facebook
将一个重要的 pull request
合并到 react
中,用基于 rollup
的构建流程取代了现有的构建系统。这引发了许多人提问:“为什么选择 rollup
而不是 webpack
?”
这个问题很合理。webpack
是现代 javascript
社区最成功的项目之一,每月有数百万次下载量,为数万个网站和应用程序提供支持。它拥有庞大的生态系统、众多贡献者,而且——这在社区开源项目中很少见——获得了实质性的财务支持。
相比之下,rollup
就像一条小鱼。但 react
并不是唯一选择 rollup
的项目——vue
、ember
、preact
、d3
、three.js
、moment
以及其他许多知名库也都在使用 rollup
。那么这是为什么呢?为什么我们不能只有一个所有人都认可的 javascript
模块打包工具呢?
A tale of two bundlers
webpack
由 Tobias Koppers
于 2012
年创建,目标是在解决现有工具无法解决的难题:构建复杂的单页应用程序(SPA)。特别是以下两个特性彻底改变了这一切:
代码分割
使得将应用拆分成可 按需加载 的可管理块成为可能。这意味着用户可以不用等待整个应用下载和解析,就可以获得交互式网站体验。你可以手动完成这项工作,实现起来会有些复杂度。
静态资源处理
图片和
css
等静态资源可以被导入到应用中,并被视为模块依赖图中的一个特殊的节点。开发者不需要再小心翼翼地将文件放在正确的文件夹中,也不需要使用特殊的脚本来为文件URL
添加哈希值,因为这些任务webpack
可以为你很好的处理。
rollup
项目创建目的则与之不同:它的核心目的是尽可能高效地构建 javascript
库的平面分发(flat distributables
)版本,实现上充分利用 ES2015
模块的巧妙设计。其他模块打包工具(例如 webpack
等)的工作方式是将每个模块包装在一个函数中,将它们放在一个包含浏览器友好的 require
实现的包中,然后逐个解析它们。如果你的应用需要 按需加载 等功能,这样做是值得的,但若不需要那么这有点浪费。而且如果你有很多模块,情况会变得更糟。
ES2015
模块启用了一种不同解析方式,也就是 rollup
所实现的方式。所有代码都放在同一个作用域中并一次性求值,从而产生更精简、更简单的代码、启动更快。你可以在 rollup
的 REPL 中自己体验这一点。
rollup
处理方式虽好但这是有代价的,代码分割 实现起来更为棘手。在撰写本文时 rollup
还不支持这个功能。同样,rollup
也不支持热模块替换(HMR
)特性。
对于使用 rollup
的人来说,可能最大的痛点是 —— 虽然 rollup
可以处理大多数 commonjs
模块(通过 @rollup/plugin-commonjs
插件),但有些东西就是无法将 commonjs
转换为 ES2015
(主要原因是 commonjs
的高度动态性和原生支持 非严格模式,而 ES2015
模块只能在严格模式下加载),但 webpack
则可以从容地处理你扔给它的任何模块。
So which should I use?
到这里,希望已经清楚为什么这两个工具可以共存并相互支持,因为他们服务于不同的目的,简而言之:
使用
webpack
构建应用程序,使用rollup
构建库
这并不是一个硬性规定,许多网站和应用程序都是用 rollup
构建的,许多库也是用 webpack
构建的。但这是一个很好的经验法则。
如果你需要代码分割,或者有很多静态资源,或者正在构建具有大量 commonjs
依赖的项目,webpack
是更好的选择。但如果你的代码库是通过 ES2015
模块编写的,而且你正在制作供其他人使用的东西,你可能更需要 rollup
。
Package authors: use pkg.module
长期以来,使用 javascript
库有点像碰运气。因为你和库作者实际上必须就模块系统达成一致。如果你使用 browserify
而库作者更喜欢 amd
,你就必须在实际构建任何东西之前将这两种模块系统粘在一起(类似在 esm
中加载 commonjs
模块时,rollup
需要将 commonjs
转换为 esm
)。通用模块定义(umd
)格式在某种程度上解决了这个问题,但因为它没有在任何地方强制执行,你永远不知道会得到什么。
ES2015
改变了这一切,因为 import
和 export
是语言的一部分。在未来,这个规范将不会有歧义,事情会运作得更加顺畅。不幸的是,由于浏览器(大多数)和 node
还不支持 import
和 export
语义,我们仍然需要提供 umd
的产物(如果你构建的产物是仅用于 node
执行,则需要 commonjs
产物)。
通过在库的 package.json
文件中添加 "module": "dist/my-library.es.js"
字段(又称 pkg.module
),现在就可以同时提供 umd
和 ES2015
。这很重要,因为 webpack
和 rollup
都可以使用 pkg.module
来生成最高效的代码——在某些情况下,他们甚至都可以将库中未使用的代码剔除掉(tree-shaking
)。
在 rollup
的 wiki 上了解更多关于 pkg.module
的信息。