在当今的前端开发环境中,快速和高效的开发工具成为了开发者的重要需求。Vite,作为一种新型的前端构建工具,凭借其快速的冷启动、即时的热模块更新(HMR)和丰富的特性,已经吸引了大量的开发者的关注。特别是其HMR的实现,相比传统的构建工具如Webpack,Vite提供了更快速、更高效的模块热更新,大大提高了开发效率。
启动服务器
当我们在本地运行 vite 命令时,Vite 会启动一个开发服务器,这个服务器将用于提供开发环境,如按需编译,热更新,代理等等。
创建文件系统监听
Vite 使用文件系统监听(file system watchers)来检测项目中的文件变化。当启动 Vite 开发服务器时,Vite 会设置一个文件系统监听器,用于监听项目中的文件变化。
文件系统监听主要使用 Node 中的一个库 chokidar 来实现。这个库可以监听文件的删除新增和修改等操作。
const watcher = chokidar.watch('file, dir, glob, or array', {
ignored: /(^|[/\])../, // ignore dotfiles
persistent: true
});
watcher
.on('add', path => rebuild)
.on('change', path => rebuild)
建立 WebSocket 连接
当浏览器加载应用程序时,Vite 客户端会与开发服务器建立一个 WebSocket 连接。这个连接用于实时接收来自服务器的更新通知和更新的模块。
重新编译模块
当一个文件被修改后,Vite 会根据不同文件类型选择相应的编译器进行编译。
-
假如是一个
.vue文件,Vite 使用对应的 Vue 编译器 @vitejs/plugin-vue 来编译,主要分为三部分:- 模版部分
@vitejs/plugin-vue使用 vue-template-compiler 将 Vue 模板编译为 JavaScript 渲染函数。这样,当组件在运行时被实例化时,渲染函数可以直接生成虚拟 DOM,提高渲染性能。 - 脚本部分
@vitejs/plugin-vue使用 @vue/compiler-sfc 对 Vue 文件中的脚本部分进行处理。这包括对 Vue 3 的新特性,如<script setup>的支持,以及对 TypeScript 的支持。 - 样式部分
@vitejs/plugin-vue使用 postcss 处理 Vue 文件中的样式部分。这包括对 CSS 预处理器(如 SCSS、LESS 等)的支持,以及对 CSS Modules 的支持。
- 模版部分
-
假如是
.jsxts或者tsx,Vite 默认会使用 ESBuild 来编译。这是一个使用Go语言开发的编译器,利用来多核处理器,速度明显优于Webpack或者Rollup。 -
.less或者.scss会使用配置的预处理器来编译。
值得一提的是,这里不仅会根据文件编译相应的模块,有必要的话,也会递归编译相关的文件,例如当我们修改了一个模块的依赖项时。
发送更新的模块
Vite 开发服务器会将更新的模块通过 WebSocket 发送给浏览器。更新的模块可能包括被修改的文件本身以及与其相关的其他文件。
浏览器接收更新的模块并应用
-
接收更新的模块:当 Vite 开发服务器通过 WebSocket 向浏览器发送更新的模块时,Vite 客户端会接收到这些模块。
-
分析模块依赖关系:Vite 客户端会分析更新的模块的依赖关系,找到需要更新的模块及其依赖的模块。
-
替换旧模块:Vite 客户端会将更新的模块替换到浏览器的模块缓存中,从而替换旧模块。这个过程涉及到以下操作:
- 删除旧模块的引用。
- 加载更新的模块。
- 更新模块缓存,使其引用新模块。
-
执行 HMR API:Vite 客户端会执行更新模块的 HMR API,如
dispose和accept回调。这些回调用于处理模块更新的副作用,例如清理资源、重新渲染组件、更新状态等。这个过程是自动执行的,但可以根据需要自定义回调函数。
if (import.meta.hot) {
import.meta.hot.dispose(() => {
// 清理资源,例如取消事件监听器
window.removeEventListener('event', eventHandler);
});
}
- 传播更新:Vite 客户端会将模块更新传播到依赖的模块。这意味着,当一个模块被更新时,所有依赖该模块的模块也会被更新。这个过程会递归地应用到整个依赖链。
写在最后
总之,Vite 作为一个高效的开发工具,为开发者提供了实时的开发体验,并简化了模块化代码的处理过程。通过了解 Vite 的原理和实现细节,开发者可以更好地利用 Vite 的功能,提高开发效率。