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 提升性能