ES基于模块的bundleless构建工具最近很流行,尤其是友达更是到处推广Vite,号称“下一代前端构建工具”。在前不久的VueConf2021上,友达可谓是大肆宣传。所以很多人曾经以为有了Vite,就不需要Webpack了。虽然现在Vite确实很火,也有很多人在试用,但现实却和大家的观念恰恰相反。目前几乎没有人使用Vite进行实际的项目开发。首先我们要先明确一个问题,Webpack为什么要打包?个人认为主要有以下三个原因:第一,由于HTTP/1.1过去受限于网络延迟和浏览器并行请求,合并压缩资源有助于减少HTTP请求,提高页面加载性能。其次,JS本身不提供模块系统。Node.js虽然基于CommonJS规范实现了一套模块化机制,但是浏览器环境并不支持。并且虽然ES2015标准在语法层面提供了模块化规范,但仍处于实现定义状态,无论是Node.js还是浏览器都不支持。直到2017年Chrome61发布,浏览器才原生支持JavaScriptModules(Node.js在14.x版本也正式支持ESModules)。因此,Webpack实际上实现了一个可以在不支持JavaScriptModules的环境下使用的模块系统,同时解决模块依赖,将模块打包成文件。第三,前端工程的核心。Webpack的核心功能是模块打包器,一开始只能处理JS模块。但是随着生态的发展,出现了各种加载器和插件,使得Webpack能够处理CSS、图片、字体等静态资源。此外,Webpack还可以在打包过程中对模块内容进行处理。典型的例子是@babel/preset-env配合core-js转换ES2015+的语法,编译兼容老浏览器。此外,它还可以编译转换.less、.vue、.jsx、.tsx等浏览器无法识别的文件格式。不仅如此,Webpack还提供了webpack-dev-server,配合HMR大大提高了开发效率。当然,Webpack也有很多痛点。因为Webpack在打包的时候需要构建模块依赖图,所以需要递归遍历所有模块。最明显的是,随着项目规模的增长,构建时间也呈线性增长,启动时间也相应增加。.其次,当我们修改其中的文件时,Webpack需要重新打包。第三,由于Webpack对打包后的源代码进行了编译压缩,代码的可读性大大降低。所以在开发环境中,我们需要使用SourceMap进行调试,而开启SourceMap无疑会降低打包速度。第四,随着项目规模的增大,打字包越来越大。过大的包裹会延长页面首屏的加载时间。Webpack对此也有解决方案,即使用TreeShaking去除无用代码(主要针对第三方库),分包策略,包括lazy-loading、SplitChunksPlugin等。那么为什么现在开始尝试解包呢?首先,主要原因是HTTP/2.0实现了多路复用和头部压缩,解决了HTTP/1.1中的队头阻塞问题。因此,通过资源整合和压缩来减少HTTP请求不会显着提高页面加载性能。其次,随着Chrome61的发布,各大浏览器都一一支持了ESM,浏览器可以直接解析import,无需自己实现模块化。使用浏览器原生ESM最大的好处就是可以按需加载,不需要打包。构建时间复杂度为O(1),启动非常快。你只需要启动devServer。与Snowpack类似,Vite也是使用浏览器原生的ESM解析import,然后向devServer请求模块,在服务端使用esbuild实时编译模块返回(主要是将.jsx,.tsx,.vue,.less等浏览器无法处理它,而不是为旧浏览器编译为ES5兼容性)。那么为什么Vite要使用esbuild进行编译呢?一个字:快!我们可以看一组对比。Webpack5打包耗时54.5s,而esbuild只需要0.37s。这是因为esbuild是用Golang写的,自然比用JS写的构建工具要快很多。同时esbuild中的算法经过精心设计,大量并行运算,充分利用CPU资源。另外,esbuild没有使用第三方依赖,内存的高效利用也是打包速度快的原因。esbuild虽然优势明显,但仍然无法替代Webpack。主要有两个原因:第一,esbuild虽然有loader,但是没有插件机制;第二,esbuild没有热更新。那么Vite在esbuild的基础上解决了热更新的问题,它能干掉Webpack吗?现在回到一开始的问题,为什么几乎没有人使用Vite进行实际项目的开发?第一个是,虽然Vite在开发环境中使用了原生的ESM,但在生产环境中仍然需要使用Rollup进行打包。即使使用HTTP/2.0,直接使用解包后的ESM仍然效率低下,因为嵌套导入会导致大量网络请求。因此,为了在生产环境中获得最佳的加载性能,最好将代码一次打包(结合TreeShaking、lazy-loading和SplitChunksPlugin等技术)。所以现在看来??,Vite更像是一个开发工具,而不是生产环境的构建工具。另外,开发环境使用浏览器ESM解析模块,而生产环境使用Rollup打包,导致本地和线上环境运行的代码不一致,线上环境的bug本地难以排查。那么有同学会问了,为什么vite不打包esbuild呢?esbuild非常快并且已经是一个非常强大的打包器,但是构建应用程序所需的一些重要功能仍在开发中——特别是代码拆分和CSS处理。目前,Rollup在这些方面更加成熟和灵活。也就是说,当esbuild将来稳定这些功能时,不排除在生产版本中使用esbuild的可能性。那么实际上Webpack每次构建都不是O(n)的复杂度。对于更新不频繁的第三方库,可以采用预构建的策略,即将lodash、moment、react等依赖提前打包并缓存在磁盘上,这样可以显着提升构建速度每次打包业务代码。Webpack4的DllPlugin可以满足这个需求。那你可能会说这个处理的粒度不够细。对于没有修改过的业务组件,也可以跳过打包,直接使用缓存。Webpack4中提供了cache-loader来实现模块缓存。Webpack5直接提供了缓存配置项,可以缓存到磁盘上。另外,Webpack的分包策略可以让打出来的Bundle不会太大。如果配置了路由懒加载,当路由发生变化时,可以通过开发者工具的Networks面板观察到异步请求模块。对比Vite,要达到O(1)的构建效率其实是非常困难的。Vite第一次启动时,会预构建依赖,并粗略打包。一方面兼容CommonJS模块,另一方面减少网络请求。如果根本不打包,会造成大量的网络请求。综上所述,Vite其实和Vue-cli是一样的。可以说,Vue正式为个人开发者和中小企业提供了脚手架工具。毕竟没必要自己造轮子。正如Vue-cli是对Webpack的封装,Vite其实是对esbuild的封装,所以“Vite能干掉Webpack”这句话本身是不对等的,充其量也差不多是作为下一代脚手架工具。而且Vite只能在开发环境使用ESM,生产环境还是需要打包的。基本上,只有在浏览器全面支持基于QUIC协议的HTTP/3之后,才有可能大规模使用ESM。更何况Webpack发展至今,loader和plugin的生态已经非常丰富,而bundleless构建工具的生态还处于起步阶段。从长远来看,ESM确实有很大的发展潜力。不排除未来bundleless构建工具有可能超越Webpack,但是Webpack在当前的前端工程中仍然扮演着非常重要的角色。参考Babel官网为什么选择webpack-webpack官方文档Vite.js中文网《不懂就问》为什么esbuild这么快?
