专注React,学不会你打我!
「代码编译打包」是如今前端工程化中绕不开的一环,这项功能依赖于「打包工具」。
最常见、受众最广的打包工具当属webpack
。
同时,在webpack
势力范围之外,存在一些在某些方面很突出的打包工具满足一部分细分领域的需求。
当我们要开发一个新项目,该使用哪种打包工具?怎么衡量打包工具的优劣呢?
本文会从几个纬度来评价一款打包工具优劣。
首先让我们简单了解下市面上常见打包工具的特点:
基于loader
与plugin
机制,接入灵活。同时由于有先发优势,有完备的社区储备。
缺点是灵活的配置造成上手成本高,遇到问题不易调试。
如果说webpack
偏向应用打包,那rollup
更偏向于库的打包。
其对ESM
更好的支持使更好的tree-shaking
能力有了原生的底层支持。
对标webpack
的繁杂配置,parcel
的的目标是「零配置完成打包」。
向开发者屏蔽配置固然利于上手,但是当默认配置无法满足需要时这种优势就会被打破。
特点是使用CJS
标准打包,使一份代码同时在node
环境与浏览器环境(打包后)执行。
其中,在浏览器环境中,node
的一些核心库(如events
、stream
、path
...)会被打包成浏览器支持的版本。
缺点:缺少ESM
标准的约束,在tree-shaking
上存在天生劣势。
基于浏览器原生支持的ESM
标准,vite
在dev
环境可以提供极快的预览效果。
同时基于go
语言编写的esbuild
,使vite
的打包速度与以上几个工具有了数量级的差异。
Google
工程师Surma[1]和其他人一起打造了一个评价打包工具优劣的开源项目tooling.report[2]。
该项目按以下5个纬度衡量打包工具优劣:
「代码分割」可以在开发者无感知(或者很少感知)的情况下,将代码拆分到不同到包,在运行时按需加载。
这种方式可以显著减少运行时需要下载和执行的JS代码。
「代码分割」包含很多因素:
Dynamic import
(动态import)比如webpack
会将动态import
语法编译为运行时以jsonp
形式加载并执行代码。
以CJS
作为打包标准的browserify
不支持ESM
,显然更不会支持动态import
。
浏览器中除了JS线程,还有worker
线程(如service worker
、web worker
)。
当使用了worker
,打包工具是否会为不同上下文打包不同的文件?
JS线程
与worker
之间,worker
与worker
之间之间是否能复用公用代码?
chunk
是否能复用引用不同入口是否能将公共的引用抽离出来只实例化一次?这里又分为「单入口应用」与「多入口应用」。
最理想的用户体验:第一次访问页面时请求静态资源数据,并缓存下来。再次请求时使用缓存数据。
这样能极大加快页面展示时间,减少服务器负荷。
但是缓存有失效/更新问题,如果静态资源已经更新,但是缓存未失效,这是很严重的问题。
当前业界主要解决方式是:静态资源本身不会失效,通过在资源url
上增加hash
来区分不同版本的资源。
这就为打包工具带来挑战:
一个「非JS资源」使用hash url
,当其发生变化,引用他的「JS文件」需要改变引用的hash url
,这可能造成该「JS文件」的hash url
改变,从而造成递归的连锁反应。
如何将这种连锁反应控制在最合理的限度?
webpack
将hash
分为hash
、content hash
、chunk hash
,就是为了以不同粒度的hash
控制连锁反应的范围。
随着ESM
规范普及,越来越多的工具开始支持导出为ESM
规范。
但是由于历史原因,很多以库都是以CJS
规范导出。
打包工具是否同时支持CJS
和ESM
?如何处理依赖文件(node_modules)中CJS
与ESM
混用的情况?
典型的web
应用不仅仅包含JS代码
,还包含HTML
、CSS
、图片
、字体
等。
如何在打包工具中处理好这些资源之间的依赖关系?
JS
有不同宿主环境,浏览器、Node
、worker
等。
Node v12
之前,Node
环境只支持CJS
规范。
大部分现代浏览器支持ESM
规范。
Web Workers
只有在chrome
中支持ESM
规范。
针对不同宿主环境,需要能打包出不同规范的产物。
针对不同类型资源,打包工具是否支持代码转换。
我们知道babel
可以将JS代码
转换为AST
,在此基础上完成诸如:
ES6
转ES5
在这一步,打包工具是否能做的更优秀?
比如基于「数据流分析」的Dead Code Elimination
(移除未使用代码)
是否能支持更多格式?
比如压缩图片、SVG
...
基于以上5个纬度,4款打包工具的得分如下:
可以看到,虽然我们时常吐槽webpack
配置让人抓狂,但是webpack
各方面确实很优秀。
颇有种带头大哥“每手都要抓,每手都要硬”的感觉。
关于每个得分标准的详细解析参考tooling.report官网[3]
[1]
Surma: https://github.com/surma
[2]
tooling.report: https://github.com/GoogleChromeLabs/tooling.report
[3]
tooling.report官网: https://bundlers.tooling.report/