w3ctech

Webpack、Rollup相爱相杀的那些事(Webpack and Rollup: the same but different)

Webpack and Rollup: the same but different

本周,Facebook将一个大的(monster)pull request合到了React master(主)分支,(这也意味着),React(以后的)源码构建流程都将会基于Rollup打包?,这也就难怪会让一些人发出酱紫的拷问:“你Facebook为啥要选择Rollup而抛弃webpack?”

有人会提出上面的问题,(我觉得)完全合乎情理的(completely reasonable)。(众所周知),Webpack是当今JavaScript社区最为成功的产出(story)之一,另外每月上百万的下载量,同时也造就了(powering)不计其数的(tens of thousands of)网站以及应用。 而且,webpack具有以下特点:

通过同webpack对比,(我们发现),Rollup真的并不起眼(Rollup is a minnow)。不过(我们也发现),在使用Rollup的道路上React并不孤单,因为Vue、Ember、Preact、D3、Three.js、Moment以及其它许多知名的库也在使用Rollup。所以,我们下一步该怎么办(what’s going on)?我们用大众认可的JavaScript模块化打包工具不行吗?

(接下来讲述的是)两个打包?(相爱相杀的)故事

早在2012年,Tobias Koppers就开始webpack这个项目啦,webpack旨在解决现存的打包工具在构建复杂的单页应用(SPA)时不能address的难题。特别是在webpack的两个特性(对代码进行分割、对静态资源进行打包)出现以后,整个打包流程被彻底颠覆。

1.在以前,把app代码分解成多个能够按需载入且可控的chunk,这几乎是不可能的。现在代码分割机制让这些变成了可能,而且这就意味着:在以前,你必须等待整个JavaScript代码下载完成,然后才能对它进行解析的局面将会被打破。换句话说,现在,你的用户可以以更短的时间来获取(交互性极强的)用户体验。当然你可以手动完成这些,不过,(我还是劝你最好不要这样做,什么?你说你就是想搞事情),好吧,(我只能)祝你好运。

2.静态资源例如图片以及CSS文件,现在不但能够被webpack打包到js文件里,而且还能够被webpack当作依赖关系图(dependency graph)的(一个)节点处理。换句话说,我们现在再也不用关心打包后文件的位置是否放对(等问题),也再也不用为了给文件URL增添hash而使用hack脚本,因为webpack会帮我们处理这些。

和webpack项目初衷不同的是,Rollup则是希望通过(利用)巧妙的ES2015模块设计,尽可能高效地构建出能够直接被其它JavaScript库引用的模块。其它的模块打包工具(包括webpack)的工作原理是酱紫的:

  • 首先,通过使用函数的方式来封装模块

  • 然后,在bundle文件中,通过使用requireAPI来引入相应的模块。(译者注:requireAPI是由webpack打包工具实现的,能够运行在浏览器)

  • 最后依次执行这些模块

如果你需要js按需载入,webpack对你来说,是个不错的选择。否则的话,使用webpack就有点浪费,而且当你的模块很多时,这种浪费现象会更加严重

ES2015模块让另一种解决方案变成可能,(忘记说了),这恰恰也是Rollup使用的模块系统。(ES2015模块系统)会把所有代码放置在统一的地方,然后一次性执行,这样做的结果是,越短、越简单的代码,Rollup启动速度越快。当然,你也可以通过Rollup REPL查看使用Rollup转换后的代码

不过我们需要在code-splitting(webpack支持的特性)以及rollup之间作出权衡,这不但是因为code-splitting机制非常难以理解,而且在我写这篇文章的时候,Rollup并不支持code-splitting特性。类似地(地方还有),Rollup不支持模块的热更新(hot module replacement)。对于人们来说,使用Rollup最大的痛点可能是,尽管Rollup通过插件可以处理大多数(使用)CommonJS(模块系统的)文件,不过有些事情并不是简单的把代码转成ES2015就行啦,再反观webpack,只要你把事情丢给它,webpack都能从容应对。

所以,我们应该使用哪种打包?呢?

到目前为止,我希望看到的是,你们能够对这两种打包?为啥能够同时存在以及为啥能够相互支撑(support each other)等问题有个清晰的认识,(好了,我不卖关子啦),这都是因为它们应用的场景不一样。 简单来讲:针对app级别的应该使用Webpack,针对js库级别的应用应该使用Rollup

不过这规则并不是一成不变的(a hard and fast):在现实中,不但有很多网站以及app是通过Rollup构建的,而且有很多库是通过webpack构建的。话说回来,对于如何选择打包工具来说,这规则是个不错的选择。

如果你确实需要代码分割特性,或者你有很多静态资源需要处理,再或者你构建的项目需要引入很多CommonJS模块的依赖,那么Webpack是个很不错的选择。如果你的代码是基于ES2015模块,而且希望你写的代码能够被其他人直接使用,你需要的打包工具可能是Rollup。

给npm包作者的建议:请使用pkg.module!

在很长一段时间,使用JavaScript库就意味着你要冒很大的风险,这是因为你和库作者使用的模块系统可能不一样,所以这就需要你和库作者在模块系统的选择上必须达成一致意见。举个例子,假设你使用的是Browserify打包工具,但是库作者更喜欢AMD模块系统,所以在你构建之前,你必须把库作者的模块系统替换成自己项目所使用的模块系统。虽说Universal Module Definition(UMD)模块系统可以稍稍解决上述问题,不过,由于UMD模块系统不强制要求你使用什么的模块系统,(这也就意味着),你永远不知道你下一步使用的模块系统是哪一种。

ES2015模块颠覆了这一切,这是因为import以及export命令已经变成JavaScript语言的一部分啦。在不久的将来,模块系统的选择将会变得更加明确,不再模棱两可啦,而且所有的JavaScript代码都能够无缝对接。不幸的是,由于浏览器(大多数)以及Node还不支持import以及export命令,所以我们仍然需要对js文件使用UMD模块系统(如果你构建的文件只是用于Node,或许可以考虑CommonJS)。

通过给你项目的package.json文件增加(针对模块)入口"module": "dist/my-library.es.js"配置,让你的项目同时支持UMD以及ES2015模块系统(的想法)变成了可能。这一点很重要,这是因为,Webpack和Rollup都会使用pkg.module来尽可能构建出高效代码。在某些情况下,Webpack以及Rollup甚至都能利用tree-shake特性来剔除项目中未使用的代码。

要想了解更多关于pkg.module细节,可以去查看Rollup wiki

真心希望,这篇文章能够让大家对这两个项目的关系有一个更清晰的认识。如果你仍然还有问题,你可以通过这些Twitter账号rich_harrisrollupjsthelarkinn找到我们。让我们一起快乐的bundling吧!

在写这篇文章的过程中,为Rich Harris所付出的劳动表示由衷的感谢。我们相信开源协作精神是共同促进 web 技术前进的源动力。

没有时间为开源项目做出自己的贡献?想通过其它方式来回馈开源项目吗?欢迎通过Open Collective众筹平台给我们webpack项目进行捐款,从而成为Webpack的支持者或赞助商。Open Collective众筹平台不但会资助核心团队,而且还会资助那些牺牲自己空闲时间帮助我们改进项目的贡献者们。

w3ctech微信

扫码关注w3ctech微信公众号

共收到0条回复