vite.png

背景

尤大知乎发文《Vite 2.0 发布了》,Vite 2 作为第一个稳定版,是时候对 Vite 做一个稍微深入点的了解了。本文旨在理解 Vite 作为新一代前端开发工具为什么能巨大的提升开发构建速度。

Vite 简介

Vite 是一款开箱即用的集成了开发服务器和打包工具的前端构建工具。
相较于传统的打包构建工具(如 Webpack)先打包构建再启动开发服务器,Vite 巧妙地利用了浏览器对 ESM 的支持,先启动开发服务器(首次启动时会执行依赖预构建,之后会介绍),当代码执行到模块加载时再请求对应模块的文件(注:对 ESM 模块的请求存在跨域问题)。官网图形象生动:
image.png
image.png

ESM

ES6 终于从语言层面提供了模块规范,只要 <script type=module > 即可使用 ES Module。ESM 包含如下特性:

  • 模块代码只在加载后执行
  • 模块只能加载一次
  • 模块是单例
  • 模块可以定义公共接口,其他模块可以基于这个公共接口观察和交互
  • 模块可以请求加载其他模块
  • 支持循环依赖
  • ES6 模块默认在严格模式下执行
  • ES6 模块不共享全局命名空间
  • 模块顶级 this 的值是 undefined(常规脚本中是 window)
  • 模块中的 var 声明不会添加到 window 对象
  • ES6 模块是异步加载和执行的

前文已提到浏览器如何使用 ESM 获取页面资源,对于 JS 代码而言,可以分为源码和依赖两类。源码是我们主要工作编辑的部分,依赖是引用的代码。我们一般使用 import React from 'react' 这种方式加载依赖,而 ESM 不支持加载裸模块的方式,Vite 会帮我们把裸模块转换成绝对路径。

历史之重

刚开始接触 Vite 时,看到使用 esbuild 预构建依赖,脑子里冒出一个问号?上面讲述的页面资源加载方式不是已经很好的解决了基于构建的服务器耗时过长的问题,为啥在开发服务器还要构建依赖呢?在仔细阅读文档后,不难发现

兼容 CommonJS 和 UMD

历史原因,很多 npm 包是使用标题的两种模块方式编写,要想变快必须承担历史之重。Vite 会扫描依赖,发现标题的两种模块方式则转换成 ESM 并缓存入 node_modules/.vite

性能

我们引入的 npm 包虽然入口文件只有一个,但是只要使用 ESM,也是会涉及到模块的加载。结果就是发请求加载 JS 文件,发现模块加载不断地请求直到加载完所有模块,极端情况是一个模块后续的请求有几百次。为了避免过多的请求影响性能,Vite 会把拥有许多内部模块依赖关系的包转换成单模块包缓存入 node_modules/.vite 。由于依赖基本不会变动,所以不管是兼容老的模块方案还是解决过多请求的转换一般都发生在首次构建。

总结

相信大家对 Vite 之所以这么快也心中有数了

  • 利用原生的 ESM,不用自己实现一套兼容各种模块标准的模块化方案,开发服务器启动后用到什么资源请求什么资源,天然的按需加载。
  • 利用 esbuild 把耗时的构建过程变成更轻量的依赖预构建,构建速度几十上百倍的提升。
  • 预构建依赖时会缓存文件,浏览器请求过的依赖也会设置强缓存,其它资源开发服务器也会根据是否变动协商缓存。

参考