Vite 的实现原理

基于 ESM 和 esbuild 的新一代构建工具,实现极速开发体验

问题

Vite 是如何实现快速的开发服务器启动和热更新的?

解答

核心原理

Vite 是基于 esbuild 与 Rollup,依靠浏览器原生 ESM 编译功能实现的新一代构建工具。它通过以下方式解决传统构建工具的性能瓶颈:

开发环境:利用浏览器原生 ES Module 能力,省略打包环节,直接将源码交给浏览器。当浏览器执行 ESM 的 import 时,向 dev server 发起模块请求,服务器对源码做简单处理后返回。

生产环境:集成 Rollup 打包生产代码,依赖其成熟稳定的生态与插件机制。

与传统构建工具的对比

Webpack 需要先将整个应用打包后,再将打包代码提供给 dev server,开发者才能开始开发。而 Vite 直接将源码交给浏览器,实现 dev server 秒开,浏览器需要相关模块时再向服务器请求,实现真正的按需加载。

依赖预构建

Vite 使用 esbuild 进行依赖预构建,解决两个问题:

模块化兼容:将依赖中的 CommonJS、UMD 等模块化规范转换成 ESM,供浏览器使用。

性能优化:将有大量内部模块的 ESM 依赖转换成单个模块,减少 import 请求次数,避免网络拥塞。

// 开发代码
import { createApp } from 'vue'

// Vite 转换后
import { createApp } from '/node_modules/vue/dist/vue.js'

缓存策略

HTTP 缓存:依赖部分使用 max-age, immutable 强缓存,源码部分使用 304 协商缓存。

文件系统缓存:预构建的依赖缓存到 node_modules/.vite,只在配置更改或手动控制时重新构建。

热更新实现

Vite 使用 chokidar 监听文件变化,通过 WebSocket 与客户端建立连接。当文件变化时,只需精确使已编辑的模块失活,无论应用大小,HMR 始终保持快速更新。

// 服务端监听文件变化
const watcher = chokidar.watch(path.resolve(root), {
  ignored: ['**/node_modules/**', '**/.git/**'],
  ignoreInitial: true,
  ignorePermissionErrors: true,
  disableGlobbing: true,
  ...watchOptions
})

// 客户端处理 WebSocket 消息
async function handleMessage(payload: HMRPayload) {
  switch (payload.type) {
    case 'connected':
      console.log(`[vite] connected.`)
      break
    case 'update':
      notifyListeners('vite:beforeUpdate', payload)
      // 重新请求模块或刷新页面
      break
  }
}

核心实现流程

  1. 启动服务:执行 npm run dev 后,调用 createServer 方法创建 HTTP 服务和 WebSocket 服务

  2. 文件监听:使用 chokidar 监听源码文件变化

  3. 模块请求:浏览器发起 import 请求时,服务器使用 es-module-lexer 扫描 import 语法,用 magic-string 重写模块路径,返回处理后的 ESM 代码

  4. 热更新:文件变化时通过 WebSocket 通知客户端,客户端重新请求变化的模块

基本使用

# 创建项目
npm create vite@latest

# 启动开发服务器
npm run dev

# 构建生产代码
npm run build

Vite 内置 6 种常用模板(vanilla、vue、react、preact、lit、svelte)及对应的 TS 版本,开箱即用。

兼容性

默认目标浏览器需支持原生 ESM 和原生 ESM 动态导入。可使用 @vitejs/plugin-legacy 插件转换为传统版本并添加 polyfill,支持旧版浏览器。

关键点

  • Vite 利用浏览器原生 ESM 能力,跳过打包环节,实现秒级启动和毫秒级热更新
  • 使用 esbuild(Go 编写)进行依赖预构建,比 Node.js 编写的编译器快几个数量级
  • 开发环境基于 ESM 按需加载,生产环境使用 Rollup 打包,两者执行代码不完全一致
  • 通过 HTTP 缓存和文件系统缓存优化性能,依赖使用强缓存,源码使用协商缓存
  • 兼容 Rollup 插件机制,官方提供 Vue、React 等框架的专用插件