likes
comments
collection
share

Webpack麻烦你不要靠的那么近,我怕Vite误会

作者站长头像
站长
· 阅读数 13

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第11天,点击查看活动详情

某天,实施人员,让我修复了一个小问题,并需要快速的发布到正式环境上,我三下五除二修改了问题;开始了打包发布,以前并没有怎么注意到启动的问题,然而这次比较急,想在用户看到之前发布替换掉内容,于是等啊等,三四分钟过去了,才打包好了;于是就开始了优化webpack构建打包,但效果有点差强人意,于是就碰到Vite,在一番实践之下,直呼"webpack麻烦你不要靠的那么近,我怕vite误会"

问:Webpack为什么启动慢?Vite为什么启动快?

答:Vite它借助了浏览器对ESM规范的支持,采取了与Webpack完全不同的unbundle机制。

  • ESM规范

    ES modules 是 JavaScript 官方的标准化模块系统。相比于 CommonJS 和 AMD 等模块规范,最新浏览器原生支持模块功能,不再需要额外打包处理。

  • bundle机制

    将项目中各种类型的源文件转化供浏览器识别的js、css、img等文件,建立源文件之间的依赖关系,并将数量庞大的源文件合并为少量的几个输出文件。

    bundle工作机制的核心部分分为两块

    • 构建模块依赖图 - module graph
    • module graph 分解为最终供浏览器使用的几个输出文件。

    强大的 bundle 机制,也引发了构建速度缓慢的问题,而且项目规模越大,构建速度越是缓慢。其主要原因是构建 module graph 的过程中,涉及到大量的文件 IO、文件 transfrom、文件 parse 操作;以及分解 module graph 的过程中,需要遍历 module graph、文件 transform、文件 IO 等。这些操作,往往需要消耗大量的时间,导致构建速度变得缓慢。

    开发模式下,dev server 需要 Webpack 完成整个工作链路才可以启动成功,这就导致构建过程耗时越久,dev server 启动越久。

    为了加快构建速度,Webpack 也做了大量的优化,如 loader 的缓存功能、webpack5 的持久化缓存等,但这些都治标不治本,只要 Webpack 的核心工作机制不变,那dev server启动优化,基本上永远都达不到Vite那样的效果。

  • unbundle机制

    顾名思义,不需要做 bundle 操作,即不需要构建、分解 module graph,源文件之间的依赖关系完全通过浏览器对 ESM 规范的支持来解析。这就使得 dev server 在启动过程中只需做一些初始化的工作,剩下的完全由浏览器支持。那有的同学就会问,源文件的resolve、load、transform、parse什么时候做呢 ? 答案是浏览器发起请求以后,dev server 端会通过 middlewares 对请求做拦截,然后对源文件做 resolve、load、transform、parse 操作,然后再将转换以后的内容发送给浏览器。

    unbundle 机制核心

    • 模块之间的依赖关系的解析由浏览器实现(ESM规范)
    • 文件的转换由 dev servermiddlewares 实现并做缓存
    • 不对源文件做合并捆绑操作
  • webpack是基于nodejs构建,js是以毫秒计数。vite是基于esbulid预构建依赖,esbulid是采用go语言编写的,go语言是纳秒级别的;所以vitewebpack打包器快10-100倍。

问:Vite的热更新为什么这么快,都到了毫秒级别了?

答:由于Vite采用unbundle机制,所以dev sever在监听到文件变化之后,只需要通过ws连接通知浏览器去重新加载变化的文件,剩下的工作就交给浏览器去做了

而webpack在监听源文件变化后,会重新编译打包;由于我们只修改了一个文件,所以只需要对这个文件做resolveloadtransformparse操作,其他文件直接使用缓存,也因此dev server响应很快,等dev server重新编译打包好,会通过ws连接通知浏览器去获取新的打包文件,然后对页面做局部的更新;

问:webpack和vite打包过程有什么不同;

webpack:

  1. 分析各模块之间的依赖
  2. 编译打包
  3. 启动服务器

vite:

  1. 启动服务器
  2. 请求模块时按需动态编译显示

Webpack麻烦你不要靠的那么近,我怕Vite误会

Webpack麻烦你不要靠的那么近,我怕Vite误会

问:Vite有什么缺点?极致的快,消耗的代价是什么?

unbundle机制给Vitedev server方面获得巨大的性能提升,但也带来了一些代价,那就是首屏和懒加载的性能下降

首屏问题

由于unbundle机制,首屏期间需要额外做一些工作

  1. 不对源文件做合并捆绑操作,导致大量的http请求
  2. dev server运行期间对源文件做resolve、load、transform、parse操作
  3. 预构建、二次预构建操作也会阻塞首屏请求,直到预构建完成为止

相对比于webpack,Vite把需要在dev server启动过程中完成的工作,转移到dev server响应浏览器请求的过程中,所以不可避免的导致首屏性能下降;不过首屏性能差只发生在dev server启动以后第一次加载页面时发生;之后reload页面时,首屏的性能会好很多,因为dev server会将之前已经完成转换的内容缓存起来

懒加载

由于 unbundle 机制,动态加载的文件,需要做 resolve、load、transform、parse 操作,并且还有大量的 http请求,导致懒加载性能也受到影响

问:resolve、load、transform、parse 操作是什么?

答:Vite 中源文件的转换是在 dev server 启动以后通过 middlewares 实现的。

当浏览器发起请求以后,dev sever 会通过相应的 middlewares 对请求做处理,然后将处理以后的内容返回给浏览器。

middlewares 对源文件的处理,分为 resolveloadtransformparser 四个过程:

  • resolve - 解析 url,找到源文件的绝对路径;
  • load - 加载源文件。如果是第三方依赖,直接将预构建内容返回给浏览器;如果是业务代码,继续 transformparser
  • transfrom - 对源文件内容做转换,即 ts -> js, less -> css 等。转换完成的内容可以直接返回给浏览器了。
  • parser - 对转换以后的内容做分析,找到依赖模块,对依赖模块做预转换 - pre transform 操作,即重复 1 - 4
  • pre transformVite 做的一个优化点。预转换的内容会先做缓存,等浏览器发起请求以后,如果已经完成转换,直接将缓存的内容返回给浏览器。

Vite 在处理步骤 3 时,是通过 esbuild.transform 实现的,对比 Webpack 使用各个 loader 处理源文件,那是非常简单、快捷的。