浅谈前端新趋势Vite以及相关实现原理

164 阅读5分钟

1. 为什么选择Vite

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

Vite有哪些特点:

  1. 快速的冷启动: No Bundle + esbuild 预构建

  2. 即时的模块热更新: 基于ESMHMR,同时利用浏览器缓存策略提升速度

  3. 真正的按需加载: 利用浏览器ESM支持,实现真正的按需加载

先给大家看两张图

vite出来之前,以前的打包工具比如Webpack是先解析依赖、打包构建再启动开发服务器,Dev Server 必须等待所有模块构建完成,当我们修改了 bundle模块中的一个子模块, 整个 bundle 文件都会重新打包然后输出。项目应用越大,启动时间越长。

bundler.37740380.png

Vite利用浏览器对ESM的支持,在启动时,内部会开启一个http server 用与拦截页面的脚本文件,当import模块时,浏览器就会下载对应模块。用到哪个,就请求哪个。没有用到的模块,不参与构建中。从而不管项目体积多大,也不会影响项目的构建速度。

esm.3070012d.png

2. esBuild 是什么?

EsBuild : An extremely fast JavaScript bundler 一个非常快速的 JavaScript 打包器

我们看到它可以将JavaScript 和TypeScript代码打包分发在网页上运行。但其打包速度却是其他工具的10~100倍。Esbuild 则选择使用 Go 语言编写,该语言可以编译为原生代码,在编译的时候都将语言转为机器语言,在启动的时候直接执行即可,JavaScript 本质上是一门单线程语言,在 CPU 密集场景下,GO天生的多线程 更具性能优势。

EsBuild的特点

  • 极速,无需缓存
  • ES6 和 CommonJS 模块
  • ES6 模块的 Tree Shaking
  • JavaScript 和 Go的API
  • TypeScript和JSX语法

image.png

想深入了解的可以看一下 esBuild 的官方文档

3.什么是 ESM ?

ESM的背景,其实是在JavaScript早期的时候,它们被用来执行独立的脚本任务,在web页面需要的地方提供一定的交互,一般不需要很大的脚本,但是随着时代的发展,慢慢的变成了使用JavaScript处理复杂的程序,所以有必要将JavaScript拆分成可按需导入的单独模块的机制。NodeJs 已经提供这方面的支持很久了还有一些其他的库也有支持。

ESM提供了更原生以及更动态的模块加载方案,最重要的就是它是浏览器原生支持的,也就是说我们可以直接在浏览器中去执行import,动态引入我们需要的模块。而且ESM模块化支持了92%以上的浏览器。

image.png

ESM的执行可以分为三个步骤

  1. 构建: 确定下载模块文件的地址,并将下载的文件解析和记录。
  2. 实例化: 将模块记录转换为模块实例,为所有的模块分配内存空间,按照导入,导出把模块指向对应的内存地址
  3. 运行: 运行并将内存空间填充。

4. Vite 的 HRM 实现原理

目前所有的打包工具实现热更新的思路都大同小异,主要是通过WebSocket创建浏览器和服务器的通信监听文件的改变,当文件被修改时,服务端发送消息通知客户端修改相应的代码,客户端对应不同的文件进行不同的操作的更新。

Vite 通过 chokidar 来监听文件系统的变更,Vite计算出热更新的边界,通过WebSocket告诉客户端需要热更新,浏览器拉取修改后的模块,执行热更新的代码,这样HMR 更新速度就不会因为应用体积的增加而变慢而 Webpack 还要经历一次打包构建。所以 HMR 场景下,Vite 表现也要好于 Webpack

整体流程图: image.png

5. 关于为什么Vite不能使用CommonJs语法

Vite 是基于原生ESM方式提供源码,实际上是让浏览器接管了打包程序的部分工作。

  1. 为什么用了ESM就不能使用CommomJs语法了呢,

    答: 了解什么是CommomJs

    答: 这里看答案, ESM模块的语法格式就是import和export

    总结: 比如HBuilderX 对于uniapp的运行支持比较好,为什么还要集成Java的环境呢。「就是优胜略汰的道理」

  2. 为什么有的第三方库就是commonjs语法,在vite中就能正常运行呢?

    答: Vite之依赖预构建

    你首次启动 vite 时,你可能会注意到打印出了以下信息:

    Pre-bundling dependencies: (正在预构建依赖:)
    react
    react-dom
    (this will be run only when your dependencies or config have changed)(这将只会在你的依赖或配置发生变化时执行)
    

    这就是 Vite 执行的所谓的“依赖预构建”。

    CommonJS 和 UMD 兼容性:  开发阶段中,Vite 的开发服务器将所有代码视为原生 ES 模块。因此,Vite 必须先将作为 CommonJS 或 UMD 发布的依赖项转换为 ESM。

    当转换 CommonJS 依赖时,Vite 会执行智能导入分析,这样即使导出是动态分配的(如 React),按名导入也会符合预期效果:

6.总结

总结一下Vite的优缺点

优点:

  1. 快速的冷启动: No Bundle + esbuild 预构建
  2. 即时的模块热更新: 基于ESMHMR,同时利用浏览器缓存策略提升速度
  3. 真正的按需加载: 利用浏览器ESM支持,实现真正的按需加载

缺点:

  1. 生态:目前Vite的生态不如Webapck,不过我觉得生态也只是时间上的问题。
  2. 生产环境由于esbuildcss和代码分割不友好使用Rollup进行打包

制作不易,感谢阅读。有什么问题欢迎评论区讨论。如有不嫌,还望关注一下公众号。感谢!!!

image.png