对 Vite 的理解与 Webpack 的区别

260 阅读9分钟

一、Vite 是什么?它与传统的前端构建工具(如Webpack)有什么区别?

Vite是一个由Evan You(Vue.js的创始人)开发的新一代前端构建工具。它专注于提供快速的开发体验和优化的构建速度。

与传统的前端构建工具,如Webpack相比,Vite有以下区别:

  1. 构建速度:Vite利用了现代浏览器原生支持的ES模块特性,将依赖模块进行预编译,使得在开发过程中无需打包,减少了构建时间和磁盘IO。这使得项目启动和热重载更加迅速,大大提升了开发效率。
  2. 开发体验:Vite支持基于原生ES模块的开发方式,可以直接在浏览器中运行源码,无需事先打包。同时,它还内置了一个非常轻量级的开发服务器,可以提供快速、自动的模块热重载,开发者可以即时看到修改后的效果。这种即时反馈的开发模式可以显著缩短开发迭代周期。
  3. 按需导入:Vite通过静态分析代码的方式,可以实现按需加载模块,只导入应用程序所需的部分代码。这使得应用程序的初始加载速度更快,同时减少了不必要的网络请求。

总的来说,Vite与传统的前端构建工具相比,更加注重开发体验和构建速度优化,使得开发者可以更加高效地进行前端开发。

二、Vite 是如何实现快速的开发服务器启动时间的?它的原理是什么?

Vite实现快速开发服务器启动时间主要依赖于以下原理:

  1. 本地开发时基于浏览器原生支持的ES模块特性:Vite利用现代浏览器对ES模块的原生支持,将每个文件作为一个独立的模块进行处理。这样,在开发过程中无需像传统的构建工具那样将所有模块打包成一个bundle文件,而是保持模块的原始状态。
  2. 针对每个模块的按需编译:当浏览器请求某个模块时,Vite会根据该模块的源码进行即时的按需编译并返回给浏览器。这样,只有被请求的模块才会被编译,减少了不必要的编译时间和资源消耗。
  3. 内置的快速开发服务器:Vite内置了一个轻量级的开发服务器,可以以单页应用的方式提供页面和静态资源的访问。该服务器支持模块热重载,可以在代码修改后实时更新页面,从而使开发者能够即时看到修改的效果。
  4. 静态代码分析:Vite通过静态分析项目中的代码,了解模块之间的依赖关系,并生成对应的依赖关系图。这种静态分析的方式允许Vite在需要编译模块时,只编译当前模块及其直接依赖的模块,而不需要重新编译整个项目。

综上所述,Vite通过利用浏览器原生支持的ES模块特性、按需编译、快速开发服务器和静态代码分析等技术手段,实现了快速的开发服务器启动时间。开发者可以在修改代码后立即看到结果,从而大大提高了前端开发的效率。

三、Vite 支持哪些前端框架?如何在 Vite 中集成这些框架?

Vite支持多种前端框架,包括但不限于以下几个常见的框架:

Vue.js:Vite对Vue.js有原生的支持。在创建新的Vue项目时,可以选择使用Vite作为脚手架工具,并自动集成了Vue的开发环境和构建配置。你可以通过以下命令创建一个基于Vue.js的Vite项目:

npm init vite@latestmy-vue-app --template vue

React:Vite也可以用于React项目的开发。类似于Vue.js,你可以使用Vite CLI来创建一个基于React的项目,并将Vite作为构建工具。使用以下命令可以创建一个基于React的Vite项目:

npm init vite@latestmy-react-app --template react

Preact:Vite同样支持Preact框架。你可以通过Vite CLI创建一个基于Preact的项目,并使用Vite进行开发和构建。使用以下命令可以创建一个基于Preact的Vite项目:

npm init vite@latestmy-preact-app --template preact

Lit:Vite也对Lit框架提供了原生支持。你可以使用Vite来开发Lit框架的应用程序,使用以下命令可以创建一个基于Lit的Vite项目:

npm init vite@latestmy-lit-app --template lit

以上是一些常见的前端框架,Vite对它们提供了原生的支持。你可以通过使用适当的模板来创建基于这些框架的Vite项目,并享受Vite带来的开发体验和性能优势。此外,如果你想在Vite中使用其他前端框架,你可以通过自定义配置进行集成和使用。

四、Vite 中的 HMR(热模块替换)是如何工作的?它在构建过程中有什么优势?

在Vite中,HMR(Hot Module Replacement,热模块替换)是一个重要的特性,它可以在开发阶段实现实时更新和快速重新加载模块,而无需完全刷新整个页面。

具体来说,Vite的HMR工作原理如下:

  1. 当应用程序启动时,Vite会创建一个WS(WebSocket)服务器,用于与浏览器建立实时通信。
  2. 当你修改了一个文件并保存时,Vite会检测到文件的变化,并将该变化推送到浏览器端。
  3. 浏览器通过WS连接接收到变化的通知后,会向Vite请求有关被更新模块的新代码。
  4. Vite根据请求返回更新后的模块代码,并通知浏览器更新相应的模块。
  5. 浏览器利用新的模块代码进行局部更新,保持页面的状态和数据不变,提供了无刷新的开发体验。

Vite的HMR在构建过程中有以下优势:

  1. 快速更新:通过HMR,Vite能够在开发阶段实现快速更新,将变更仅限于修改的模块,而不需要重新构建和刷新整个页面。这大大缩短了调试和开发周期,提高了开发效率。
  2. 保持状态:HMR能够在模块更新时保持应用程序的状态和数据。当你修改一个模块后,不会丢失已有的应用状态,使得开发过程更加流畅。
  3. 模块级热更新:Vite的HMR能够实现模块级别的热替换。这意味着你可以在不影响其他模块的情况下,只更新当前修改的模块,从而最小化代码刷新带来的影响。
  4. 更少的构建开销:由于Vite不需要在每次文件修改时重新构建整个应用程序,因此在开发过程中能够减少构建开销,提高构建速度。

综上所述,Vite的HMR使得前端开发者在开发阶段能够更加高效地进行模块修改,快速查看结果并保持应用程序的状态,极大地提升了开发体验和开发效率。

五、为什么说 vite 比 webpack 要快

webpack 对比,为什么 vite 的冷启动、热启动、热更新都会快?这就要说说二者的区别。

使用 webpack 时,从 yarn start 命令启动,到最后页面展示,需要经历的过程:

  1. entry 配置项为起点,做一个全量的打包,并生成一个入口文件 index.html 文件;
  2. 启动一个 node 服务;
  3. 打开浏览器,去访问入 index.html,然后去加载已经打包好的 jscss 文件;

在整个工作过程中,最重要的就是第一步中的全量打包,中间涉及到构建 module graph (涉及到大量度文件操作、文件内容解析、文件内容转换)、chunk 构建,这个需要消耗大量的时间。尽管在二次启动、热更新过程中,在构建 module graph 中可以充分利用缓存,但随着项目的规模越来越大,整个开发体验也越来越差。

在浏览器支持 ES 模块之前,JavaScript 并没有提供原生机制让开发者以模块化的方式进行开发。这也正是我们对 “打包” 这个概念熟悉的原因:使用工具抓取、处理并将我们的源码模块串联成可以在浏览器中运行的文件。诸如 webpackRollupParcel 等工具应运而生。

Vite 旨在利用生态系统中的新进展解决上述问题:浏览器开始原生支持 ES 模块,且越来越多 JavaScript 工具使用编译型语言编写。

使用 vite 时, 从 vite 命令启动,到最后的页面展示,需要经历的过程:

  1. 使用 esbuild 预构建依赖,提前将项目的第三方依赖格式化为 ESM 模块;
  2. 启动一个 node 服务;
  3. 打开浏览器,去访问 index.html
  4. 基于浏览器已经支持原生的 ESM 模块, 逐步去加载入口文件以及入口文件的依赖模块。浏览器发起请求以后,dev server 端会通过 middlewares 对请求做拦截,然后对源文件做 resolveloadtransformparse 操作,然后再将转换以后的内容发送给浏览器。

在第四步中,vite 需要逐步去加载入口文件以及入口文件的依赖模块,但在实际应用中,这个过程中涉及的模块的数量级并不大,需要的时间也较短。而且在分析模块的依赖关系时, vite 采用的是 esbuild,esbuild 使用 Go 编写,比以 JavaScript 编写的打包器预构建依赖快 10-100 倍(webpack 就是采用 js

综上,开发模式下 vitewebpack 快的原因:

  1. vite 不需要做全量的打包,这是比 webpack 要快的最主要的原因;
  2. vite 在解析模块依赖关系时,利用了 esbuild,更快(esbuild 使用 Go 编写,并且比以 JavaScript 编写的打包器预构建依赖快 10-100 倍);
  3. 按需加载;模块之间的依赖关系的解析由浏览器实现。Vite 只需要在浏览器请求源码时进行转换并按需提供源码。根据情景动态导入代码,即只在当前屏幕上实际使用时才会被处理。
  4. 充分利用缓存;Vite 利用 HTTP 头来加速整个页面的重新加载(再次让浏览器为我们做更多事情):源码模块的请求会根据 304 Not Modified 进行协商缓存,而依赖模块请求则会通过 Cache-Control: max-age=31536000,immutable 进行强缓存,因此一旦被缓存它们将不需要再次请求。