Vite 的介绍和发展状况

834 阅读9分钟

「这是我参与2022首次更文挑战的第13天,活动详情查看:2022首次更文挑战」。

1. Vite 的介绍和发展状况

目前,Webpack 是整个前端使用最多的构建工具,但是除了 Webpack,还有一些其它的构建工具,比如 rollup(常用来打包框架)、parcel(号称零配置的工具,但是本身比较大,用得比较少一点)、gulp(一般用来做自动化的东西)、vite(尤雨溪 2020 年出的下一代前端工具) 等等。

什么是 Vite 呢?

官方的定位是:下一代前端(开发与构建)工具。

“下一代”的意思是领先于当前这一代(Webpackrollup 等工具)。

如何定义下一代开发和构建工具呢?

  • 在实际开发中,我们编写的代码往往是不能被浏览器直接识别的,比如 ES6TypeScriptVue 文件等等;
  • 所以我们必须通过构建工具来对代码进行转换(编译),类似的工具有 Webpackrollupparcel
  • 但是随着项目越来越大,需要处理的 JavaScript 代码越来越多,模块越来越多;
  • 构建工具就需要很长的时间才能开启服务器,HMR 可能也需要几秒钟(官方的说法,但一般 1 秒钟内 HMR 就能生效了)才能在浏览器中反应出来;
  • 总之,当项目比较大时,Webpack 等构建工具构建项目的时间是比较长的,再加上 Webpack 的配置比较复杂,有点难,所以也有这样的说法:天下苦 Webpack 久矣;

因此,尤雨溪开发了一个新的工具:Vite(法语意为 “快速的”,发音 /vit/),一种新型前端构建工具,能够显著提升前端开发体验。

Vite 主要由两部分组成:

  • 一个开发服务器(即在开发阶段,Vite 本质上就是一个开发服务器),它基于原生 ES 模块提供了丰富的内建功能,HMR 的速度非常快速;
  • 一套构建指令(即在打包阶段使用的一套构建命令),它使用 RollupVite 已经内置了 Rollup打包我们的代码,并且它是预配置的,可以输出生产环境的优化过的静态资源;

那么,既然 Vite 这么棒,我们现在是否就能放弃 Webpack,直接学习 Vite 了呢?

  • 先说 Webpack,目前我们还不能放弃 Webpack 的学习,因为前端三大框架的脚手架都依然是依赖的 Webpack,而且 Webpack 本身的生态很完善,它未来的发展也可能会推出更好的功能,所以目前 Webpack 依然是社区中使用的最多的一个构建工具。因此,我们还是有必要学习 Webpack

  • 再来说 Vite,我个人非常看好 Vite 的未来,也希望它能有更好的发展;

  • 但是,目前 Vite 虽然已经更新到 2.x 版本,但依然不算非常稳定,并且比较少大型项目(或框架)使用 Vite 来进行构建(目前开发中可以使用 Vue3 了,但 Vite 可以再等一等);

  • Vite 的整个社区插件等支持也还不够完善(如果你遇到某个需求,想要对某个东西进行打包,但是它需要某个插件的支持,那么你可能需要自己去从零开始编写这个插件,但如果用的是 Webpack,基本上就不会出现需要自己从零编写插件的情况,因为你遇到的任何需求基本上都能在 Webpack 的社区中找到对应的插件或 Loader 来帮我们实现);

  • 包括 Vue 脚手架本身,目前也还没有迁移到 ViteVueVite 都是同一个作者开发的),使用的还是 Webpack(虽然后期一定是有这个打算的);

  • 所以 Vite 看起来非常的火热,在面试中也可能经常会问到,但是实际项目中应用的还比较少;

2. 浏览器原生支持 ES modules 但存在的弊端

我们新建一个项目,项目的目录结构如下:

image-20211218133911560

其中,src/main.js 文件中的内容如下:

import { sum } from './js/math'

console.log('Hello World');
console.log(sum(10, 20));

src/js/math.js 文件中的内容如下:

export function sum(num1, num2) {
  return num1 + num2;
}

index.html 文件中的内容如下:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  
  <script src="./src/main.js"></script>
</body>
</html>

当前,这个 vite的基本使用 项目中没有使用任何的构建工具,在没有使用构建工具的情况下,我们把项目跑起来看看有没有问题:我们通过 Live Server 插件(开启一个本地服务器)在浏览器中打开项目目录下的 index.html 文件:

image-20211218135936104

你会发现,浏览器控制台中报错了。我们知道,对于比较新的现代浏览器,特别是 Chrome 浏览器比较新的版本,它们都已经支持 ES 模块化(ES modules,简写:ESM)了,也就是说我们前面代码中的 importexport 语法浏览器都能解析了。那为什么这里依然报错了呢?原因很简单,如果我们想让浏览器成功解析代码中的 ESM 代码,必须在相应的 <script> 元素上添加 type="module",以便到时候在加载这个 <script> 元素时告诉浏览器它里面的代码是模块类型的代码,那么浏览器就能正确解析了。

所以,我们将 index.html 中的 <script> 元素对应的那行代码修改如下:

<script src="./src/main.js" type="module"></script>

再来看浏览器中的效果:

image-20211218141344798

可以看到,控制台又报了新的错误,意思是找不到 src/js/math。这是为什么?因为如果我们使用的是原生的 ESM,那么项目的代码中,遇到引用文件路径的地方,文件的后缀名(如 .js.json.ts 等等)就不能省略了。你可能会问,那之前在 Webpack 中怎么可以省略呢?之前讲 Webpack 如何查找路径时我们讲过,Webpack 中是使用了 enhanced-resolve 这个库来解析文件路径的,它有自己的一套路径解析规则,当路径中没有 .js 后缀名时,它相当于会给我们加上。但我们这里没有使用 Webpack,所以如果我们自己没有写上文件后缀,浏览器是不会帮我们加上的,因此,浏览器最终会找不到对应的文件。

所以,我们将 src/main.js 文件中的 import 语句修改如下:

import { sum } from './js/math.js'

再来看浏览器中的效果:

image-20211218142837974

现在总算是成功了。这么看来,既然浏览器支持 ESM 的代码,那么如果我们开发中编写的是一些 ES6 的代码,那开发阶段不就可以不用构建工具,直接把代码跑在浏览器上就可以了吗?这也省去了构建过程。等到真正准备打包上线时,我们再对代码进行构建,打包成 ES5 的代码(因为我们要适配更多的浏览器,用户使用的可能是较低版本的浏览器,不一定支持 ESM)。这样做可以省去开发阶段的多次构建过程,提高开发效率。而这便是下一代前端开发与构建工具 Vite 的基本思想。

那你可能会想,既然浏览器能解析 ESM 了,那不是就不需要构建工具了吗?这么想的话问题考虑得就不周全了,因为你只考虑了 ES6 的代码,但是在开发阶段,除了 ES6 的代码,还有可能有 ts 代码、.vue 文件的代码、.less 文件的代码等等,这些代码即使是最新的浏览器也是不能识别的。那浏览器不认识这些代码该怎么办呢?这就是 Vite 要帮我们解决的问题,Vite 会简单地对这些代码做转化,转化为浏览器可以识别的 ESM 代码(Vite 内部会开启一个 Connect 服务器,会对浏览器请求的代码文件做一个转发,转发到去请求已经转化好的 ESM 的代码,之后浏览器请求到的是这些转化后的 ESM 代码,然后直接执行这些代码就可以了)。

所以,我们这里没有使用构建工具的情况下,直接使用 ESM,也是可以正常地跑在浏览器上的。但是,一旦项目复杂了,存在 .ts.vue 等文件时,代码在浏览器上就不能正常跑了,所以我们依然是需要去使用构建工具的。

此外,我们再来演示一个东西,我们在命令行终端中 cdvite的基本使用 目录下,执行 npm init 命令:

image-20211218150753949

用不带中文的字符重命名包名后,一路回车到底,在项目目录下生成 package.json 文件(这里目前和构建工具没有任何关系),用来帮助我们管理项目中的依赖包。然后,我们执行下面的命令安装 lodash-es(作为 ES modules 导出的 Lodash 库,Lodash 是一个 JS 工具库,里面有很多好用的工具函数):

npm install lodash-es

安装完之后,我们在 src/main.js 中导入它,然后使用它里面的 join() 方法:

import _ from 'lodash-es'
import { sum } from './js/math.js'

console.log('Hello World');
console.log(sum(10, 20));

console.log(_.join(['abc', 'cba'], '-'));

之后我们来到浏览器中看下效果:

image-20211218152446347

你会发现,浏览器加载 lodash-es 模块失败了,这是因为我们是直接把代码交给了浏览器去加载,浏览器可不认识这种写法(在 Webpack 中之所以能加载这种模块,是因为 Webpack 使用了 enhanced-resolve 包进行了解析)。所以,我们可以改成相对路径的写法:

import _ from '../node_modules/lodash-es/lodash.default.js'

再来看浏览器中的效果:

image-20211218153137028

现在就没问题了。

可见,第三方包如果支持 ES modules 的话,也是可以直接跑在浏览器上面的。但是,这里的 lodash-es 包跑在浏览器上有个很大的弊端,我们可以打开浏览器的 Network 面板,刷新页面看下网络请求:

image-20211218153700775

你会发现,这里发送了几百个网络请求。这是因为我们导入的 lodash.default.js 文件中又依赖了很多其它文件,而浏览器会认为这些文件也都需要加载,因此就会发送很多次请求(被依赖的每个 js 文件都会发送一次请求)。而浏览器发送上百次请求,之后再解析上百个文件,必然会非常消耗性能的。

综上,如果我们不借助其它(构建)工具,虽然可以直接使用 ES modules 来开发,但是会带来两个问题:

  1. 首先,我们的代码中如果有 TypeScriptlessvue 等代码时,浏览器并不能直接识别(即某些文件不能识别);
  2. 其次,我们会发现在使用 lodash 时,加载了上百个模块的 js 代码,浏览器会发送上百个请求,这是巨大的性能消耗(即如果包之间依赖太多,那么会发送过多的网络请求,导致性能消耗很大);

Vite 就帮我们解决了上面这两个问题。