Vite 原理与性能优势
Vite 的工作原理及为什么比 Webpack 启动快
问题
Vite 的工作原理是什么?为什么它的开发服务器启动速度比 Webpack 快很多?
解答
Webpack 的问题
Webpack 在开发模式下需要先打包整个应用,再启动开发服务器:
入口文件 → 分析依赖 → 打包所有模块 → 启动服务器 → 可访问
项目越大,启动越慢。
Vite 的做法
Vite 利用浏览器原生 ESM 支持,跳过打包步骤:
启动服务器 → 可访问 → 按需编译请求的模块
// 浏览器直接请求 ESM 模块
// index.html
<script type="module" src="/src/main.js"></script>
// main.js - 浏览器发起请求
import { createApp } from 'vue' // 请求 /node_modules/.vite/vue.js
import App from './App.vue' // 请求 /src/App.vue
createApp(App).mount('#app')
三个关键技术
1. 原生 ESM
Vite 让浏览器自己处理模块加载:
// Vite 开发服务器返回的代码
// 浏览器看到 import 语句会自动发起新请求
import { ref } from '/@modules/vue'
import Comp from '/src/components/Comp.vue?t=123456'
2. 按需编译
只编译当前页面用到的模块:
// Vite 中间件伪代码
async function transformMiddleware(req, res) {
const url = req.url
// 只有请求到达时才编译
if (url.endsWith('.vue')) {
const code = await fs.readFile(url)
const result = await compileSFC(code) // 编译 Vue 单文件组件
res.send(result)
}
if (url.endsWith('.ts')) {
const code = await fs.readFile(url)
const result = await esbuild.transform(code, { loader: 'ts' })
res.send(result.code)
}
}
3. Esbuild 预构建
用 Go 编写的 Esbuild 处理 node_modules,速度是 JavaScript 打包工具的 10-100 倍:
// vite.config.js
export default {
optimizeDeps: {
// 预构建配置
include: ['lodash-es', 'axios'], // 强制预构建
exclude: ['my-local-package'] // 排除预构建
}
}
预构建做两件事:
// 1. 将 CommonJS 转为 ESM
// node_modules/lodash/index.js (CommonJS)
module.exports = { debounce, throttle }
// 转换后 → .vite/lodash.js (ESM)
export { debounce, throttle }
// 2. 合并小模块,减少请求数
// lodash-es 有几百个文件,预构建合并成一个
import { debounce } from 'lodash-es'
// 只需要一个请求,而不是几百个
对比示意
Webpack 开发模式:
┌─────────────────────────────────────────────────┐
│ 启动时打包全部模块 (可能几十秒) │
│ entry → moduleA → moduleB → ... → bundle.js │
└─────────────────────────────────────────────────┘
Vite 开发模式:
┌──────────────┐
│ 启动服务器 │ (几百毫秒)
└──────────────┘
↓
┌──────────────┐
│ 请求 main.js │ → 编译 main.js
└──────────────┘
↓
┌──────────────┐
│ 请求 App.vue │ → 编译 App.vue
└──────────────┘
↓
...按需继续
HMR 也更快
// Webpack HMR: 重新打包受影响的模块链
// 改动 utils.js → 重新打包 utils.js + 所有依赖它的模块
// Vite HMR: 只更新改动的模块
// 改动 utils.js → 只发送 utils.js 的新内容
// 浏览器通过 ESM 动态替换
关键点
- 原生 ESM:利用浏览器原生模块系统,无需打包,直接请求源文件
- 按需编译:只编译当前页面请求的模块,未访问的代码不处理
- Esbuild 预构建:用 Go 编写,将 node_modules 转换为 ESM 并合并,速度极快
- 启动时间与项目大小无关:不管项目多大,启动都是毫秒级
- 生产环境仍用 Rollup:开发用 ESM,生产打包保证兼容性
目录