Vite 工作原理
基于 ESM 和 esbuild 的新一代前端构建工具实现原理
问题
Vite 是如何实现快速的开发服务器启动和热更新的?
解答
核心机制
Vite 是基于 esbuild 与 Rollup,依靠浏览器原生 ESM 编译功能实现的构建工具。它通过以下方式解决传统构建工具的性能瓶颈:
开发环境:利用浏览器原生 ES Module 能力,省略打包环节,直接向浏览器提供源码。当浏览器执行 ESM 的 import 时,向 dev server 发起模块请求,服务器对源码做简单处理后返回。
生产环境:集成 Rollup 打包代码,依赖其成熟的生态和插件机制。
与传统构建工具的区别
Webpack 需要先将整个应用打包后,再将打包后的代码交给 dev server,开发者才能开始开发。大型项目启动时间可达几十秒甚至几分钟。
Vite 直接将源码交给浏览器,实现 dev server 秒开。浏览器需要相关模块时,再向 dev server 发起请求,服务器简单处理后返回该模块,实现真正的按需加载。
依赖预构建
Vite 使用 esbuild 进行依赖预构建,解决两个问题:
模块化兼容:将依赖中的 CommonJS、UMD 等模块化规范转换成 ESM,供浏览器使用。
性能优化:将有大量内部模块的 ESM 依赖转换成单个模块,减少 import 请求次数。例如 lodash-es 有超过 600 个内置模块,预构建后只需一次请求。
// 开发代码
import { createApp } from 'vue'
// Vite 转换后
import { createApp } from '/node_modules/vue/dist/vue.js'
缓存策略
HTTP 缓存:依赖部分使用 max-age, immutable 强缓存,源码部分使用 304 协商缓存。
文件系统缓存:预构建的依赖缓存到 node_modules/.vite,只有在配置更改或手动控制时才重新构建。
HMR 热更新
Vite 的 HMR 基于原生 ESM 执行。编辑文件时,Vite 只需精确地使已编辑的模块失活,无论应用大小如何,都能保持快速更新。
核心实现
Vite 通过 WebSocket 与客户端建立连接实现热更新:
服务端(vite/packages/vite/src/node):
// 创建服务器
export async function createServer(inlineConfig = {}) {
// 整合配置
const config = await resolveConfig(inlineConfig, 'serve', 'development')
// 创建 HTTP 服务
const httpServer = await resolveHttpServer(serverConfig, middlewares, httpsOptions)
// 创建 WebSocket 服务
const ws = createWebSocketServer(httpServer, config, httpsOptions)
// 监听文件变化
const watcher = chokidar.watch(path.resolve(root), {
ignored: ['**/node_modules/**', '**/.git/**'],
ignoreInitial: true,
...watchOptions
})
return server
}
客户端(vite/packages/vite/src/client):服务启动时向浏览器注入代码,处理 WebSocket 消息。
async function handleMessage(payload) {
switch (payload.type) {
case 'connected':
console.log(`[vite] connected.`)
break
case 'update':
// 重新请求模块
break
case 'full-reload':
// 刷新页面
break
}
}
esbuild 编译速度
esbuild 使用 Go 编写,在 CPU 密集型任务下性能优势明显,编译速度比 Node.js 编写的编译器快几个数量级。
基本使用
# 创建项目
npm create vite@latest
# package.json 脚本
{
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
}
}
Vite 内置 6 种常用模板(vanilla、vue、react、preact、lit、svelte)及对应的 TypeScript 版本。
优势与不足
优势:
- 开发服务器秒开,热更新毫秒响应
- 高度集成,开箱即用
- 基于 esbuild 的依赖预处理,速度极快
- 兼容 Rollup 插件机制
- 天然支持 TypeScript
- 内置 SSR 支持
不足:
- 对 Vue 的支持优于 React
- 生产环境使用 Rollup 打包,与开发环境代码不一致
- 市场实践案例相对较少
浏览器兼容性
默认目标浏览器需支持原生 ESM 和原生 ESM 动态导入。可使用 @vitejs/plugin-legacy 插件转换为传统版本并提供 polyfill。
关键点
- Vite 利用浏览器原生 ESM 能力,跳过打包环节,实现开发服务器秒开和按需加载
- 使用 esbuild 进行依赖预构建,将 CommonJS/UMD 转换为 ESM,并合并大量内部模块减少请求
- 通过 WebSocket 实现精确的模块级热更新,编辑文件时只使已编辑模块失活
- 开发环境基于 ESM,生产环境使用 Rollup 打包,充分利用两者优势
- 采用强缓存和协商缓存策略,依赖缓存到
node_modules/.vite提升性能
目录