Vite(法语意为”快”)不仅仅是另一个构建工具——它代表了前端构建工具的范式转变。从 Vue 生态诞生,到成为 React、Svelte、Astro 等框架的首选,Vite 正在重新定义开发体验。本文将带你深入理解 Vite 快速的秘密。
传统构建工具的痛点
Webpack 时代的问题
传统打包流程:
┌─────────────────────────────────────────────────────┐
│ 启动开发服务器 │
│ ↓ │
│ ┌─────────────────────────────────────────────┐ │
│ │ 打包整个应用(Bundle) │ │
│ │ entry.js → 解析依赖 → 转换 → 合并 → bundle │ │
│ │ │ │
│ │ 即使改动一行代码,也可能需要重新打包大量模块 │ │
│ └─────────────────────────────────────────────┘ │
│ ↓ │
│ 服务器就绪(30s+) │
└─────────────────────────────────────────────────────┘
问题:
- 启动慢:需要打包整个应用才能启动
- HMR 慢:改动后需要重新构建受影响的模块
- 随项目增长恶化:项目越大,等待时间越长
Vite 的核心创新
1. 原生 ESM 开发
Vite 利用浏览器原生支持的 ES Modules,跳过了打包步骤:
Vite 开发模式:
┌─────────────────────────────────────────────────────┐
│ 启动开发服务器 │
│ ↓ │
│ ┌─────────────────────────────────────────────┐ │
│ │ 不打包!直接启动服务器 │ │
│ │ 预构建依赖(仅第三方库) │ │
│ └─────────────────────────────────────────────┘ │
│ ↓ │
│ 服务器就绪(<1s) │
│ ↓ │
│ ┌─────────────────────────────────────────────┐ │
│ │ 浏览器请求模块时,按需编译和提供 │ │
│ │ GET /src/App.vue → 实时编译 → 返回 ESM │ │
│ └─────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────┘
<!-- 浏览器直接加载 ES 模块 -->
<script type="module" src="/src/main.js"></script>
// main.js - 浏览器原生理解这些导入
import { createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')
2. 依赖预构建(esbuild)
Vite 使用 esbuild(Go 编写)预构建第三方依赖:
// vite.config.js
export default {
optimizeDeps: {
include: ['lodash-es', 'axios'], // 强制预构建
exclude: ['your-local-package'], // 排除预构建
}
}
为什么需要预构建?
- CommonJS 转 ESM:很多 npm 包仍使用 CommonJS
- 合并小模块:lodash-es 有 600+ 个模块,预构建合并为单个
- 缓存优化:预构建结果缓存在
node_modules/.vite
esbuild vs 其他工具:
┌────────────────────────────────────────┐
│ 工具 │ 打包 three.js 时间 │
├────────────────────────────────────────┤
│ esbuild │ 0.37s │
│ Rollup │ 32.07s │
│ Webpack 5 │ 41.53s │
│ Parcel 2 │ 56.71s │
└────────────────────────────────────────┘
esbuild 快 100 倍!
3. 闪电般的 HMR
// 传统 HMR:重新构建整个模块图
moduleA.js 改动
→ 重新构建 moduleA
→ 检查依赖 moduleA 的模块
→ 重新构建这些模块
→ ...递归...
→ 发送更新到浏览器
// Vite HMR:精确更新
moduleA.js 改动
→ 只让 moduleA 失效
→ 浏览器重新请求 moduleA
→ 服务器即时编译返回
// 自定义 HMR 处理
if (import.meta.hot) {
import.meta.hot.accept('./module.js', (newModule) => {
// 处理模块更新
console.log('Module updated:', newModule);
});
// 清理副作用
import.meta.hot.dispose(() => {
cleanup();
});
}
核心配置详解
基础配置
// vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import path from 'path'
export default defineConfig({
// 插件
plugins: [vue()],
// 路径别名
resolve: {
alias: {
'@': path.resolve(__dirname, 'src'),
'@components': path.resolve(__dirname, 'src/components'),
},
},
// 开发服务器
server: {
port: 3000,
open: true,
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ''),
},
},
},
// 构建配置
build: {
target: 'es2020',
outDir: 'dist',
sourcemap: true,
minify: 'esbuild', // 或 'terser'
rollupOptions: {
output: {
manualChunks: {
vendor: ['vue', 'vue-router'],
utils: ['lodash-es', 'dayjs'],
},
},
},
},
// CSS 配置
css: {
preprocessorOptions: {
scss: {
additionalData: `@import "@/styles/variables.scss";`,
},
},
modules: {
localsConvention: 'camelCase',
},
},
})
环境变量
# .env
VITE_API_URL=https://api.example.com
VITE_APP_TITLE=My App
// 在代码中使用
console.log(import.meta.env.VITE_API_URL);
console.log(import.meta.env.MODE); // 'development' 或 'production'
console.log(import.meta.env.DEV); // true 或 false
console.log(import.meta.env.PROD); // true 或 false
// env.d.ts - 类型声明
/// <reference types="vite/client" />
interface ImportMetaEnv {
readonly VITE_API_URL: string
readonly VITE_APP_TITLE: string
}
interface ImportMeta {
readonly env: ImportMetaEnv
}
插件系统
Vite 插件兼容 Rollup 插件接口,同时扩展了 Vite 特有的钩子。
插件结构
// my-vite-plugin.js
export default function myPlugin(options = {}) {
return {
name: 'my-plugin',
// Vite 特有钩子
configResolved(config) {
// 配置解析完成后
console.log('Final config:', config);
},
configureServer(server) {
// 配置开发服务器
server.middlewares.use((req, res, next) => {
// 自定义中间件
next();
});
},
transformIndexHtml(html) {
// 转换 HTML
return html.replace(
'</head>',
`<script>console.log('Injected!')</script></head>`
);
},
// Rollup 兼容钩子
transform(code, id) {
if (id.endsWith('.custom')) {
return {
code: transformCustomFormat(code),
map: null,
};
}
},
// 构建钩子
buildStart() {
console.log('Build started');
},
buildEnd() {
console.log('Build finished');
},
};
}
常用官方插件
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import react from '@vitejs/plugin-react'
import legacy from '@vitejs/plugin-legacy'
export default defineConfig({
plugins: [
// Vue 支持
vue(),
// React 支持(包含 Fast Refresh)
react(),
// 旧浏览器兼容
legacy({
targets: ['defaults', 'not IE 11'],
}),
],
})
高级特性
1. Glob 导入
// 同步导入:构建时静态分析
const modules = import.meta.glob('./modules/*.js', { eager: true });
/*
{
'./modules/a.js': { default: ... },
'./modules/b.js': { default: ... },
}
*/
// 异步导入:懒加载
const lazyModules = import.meta.glob('./modules/*.js');
/*
{
'./modules/a.js': () => import('./modules/a.js'),
'./modules/b.js': () => import('./modules/b.js'),
}
*/
// 使用
for (const path in lazyModules) {
const module = await lazyModules[path]();
module.default.init();
}
2. Web Workers
// 内联 worker
import MyWorker from './worker?worker&inline'
const worker = new MyWorker()
// 带共享 worker
import SharedWorker from './shared-worker?sharedworker'
// worker 内部可以使用 import
// worker.js
import { heavyComputation } from './utils'
self.onmessage = (e) => {
const result = heavyComputation(e.data)
self.postMessage(result)
}
3. WebAssembly
// 导入 wasm
import init, { greet } from './hello.wasm?init'
const instance = await init()
greet('World')
// 或者获取原始 URL
import wasmUrl from './hello.wasm?url'
const response = await fetch(wasmUrl)
4. SSR 支持
// vite.config.js
export default defineConfig({
build: {
ssr: true,
},
ssr: {
// 这些依赖不会被外部化
noExternal: ['vue', '@vueuse/core'],
// 这些依赖会被外部化
external: ['fsevents'],
},
})
// server.js
import { createServer } from 'vite'
import express from 'express'
const app = express()
const vite = await createServer({
server: { middlewareMode: true },
appType: 'custom',
})
app.use(vite.middlewares)
app.use('*', async (req, res) => {
const url = req.originalUrl
const template = await vite.transformIndexHtml(url, html)
const { render } = await vite.ssrLoadModule('/src/entry-server.js')
const appHtml = await render(url)
const finalHtml = template.replace('<!--app-html-->', appHtml)
res.send(finalHtml)
})
性能优化技巧
1. 代码分割策略
// vite.config.js
export default defineConfig({
build: {
rollupOptions: {
output: {
manualChunks(id) {
// 第三方库单独打包
if (id.includes('node_modules')) {
// 大型库单独分块
if (id.includes('lodash')) return 'lodash'
if (id.includes('chart.js')) return 'charts'
// 其他 vendor
return 'vendor'
}
},
},
},
},
})
2. 按需加载
// 路由懒加载
const routes = [
{
path: '/',
component: () => import('./pages/Home.vue'),
},
{
path: '/about',
component: () => import('./pages/About.vue'),
},
]
// 条件加载
const loadHeavyFeature = async () => {
const { HeavyChart } = await import('./HeavyChart')
return HeavyChart
}
3. 预加载
<!-- Vite 自动生成预加载指令 -->
<link rel="modulepreload" href="/assets/vendor.js">
// 手动预加载
import.meta.glob('./pages/*.vue', {
eager: false,
import: 'default',
})
与其他工具对比
| 特性 | Vite | Webpack | Parcel |
|---|---|---|---|
| 冷启动 | ~300ms | 10-60s | 5-30s |
| HMR | <50ms | 1-10s | 1-5s |
| 生产构建 | Rollup | Webpack | Parcel |
| 配置复杂度 | 低 | 高 | 极低 |
| 插件生态 | Rollup 兼容 | 最丰富 | 有限 |
| 原生 ESM | ✅ | ❌ | ❌ |
迁移指南
从 Create React App 迁移
# 1. 安装依赖
npm install vite @vitejs/plugin-react -D
# 2. 创建 vite.config.js
// vite.config.js
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [react()],
})
// package.json
{
"scripts": {
- "start": "react-scripts start",
- "build": "react-scripts build",
+ "dev": "vite",
+ "build": "vite build",
+ "preview": "vite preview"
}
}
<!-- index.html 移动到根目录 -->
<!DOCTYPE html>
<html>
<head>
<title>My App</title>
</head>
<body>
<div id="root"></div>
<!-- 添加入口脚本 -->
<script type="module" src="/src/main.jsx"></script>
</body>
</html>
总结
Vite 之所以快,核心在于:
| 技术 | 作用 |
|---|---|
| 原生 ESM | 跳过打包,按需编译 |
| esbuild | 超快的依赖预构建 |
| 精确 HMR | 只更新变化的模块 |
| Rollup | 生产构建优化 |
关键收获:
- 开发时不打包,利用浏览器原生 ESM
- esbuild 预构建让依赖加载飞快
- HMR 更新精确到模块级别
- 插件系统兼容 Rollup 生态
- 生产构建仍使用成熟的 Rollup
Vite 不只是”更快的 Webpack”,它代表了一种全新的开发理念:拥抱浏览器原生能力,把复杂留给构建时,把速度还给开发者。
开发体验的提升不是奢侈,而是生产力的保障。选择 Vite,让等待成为过去。