Vite 3 is here. The jump from Vite 2 to 3 is more than just a version bump — faster cold starts, a better developer experience, and a more mature plugin ecosystem. As the person on our team responsible for build-tooling decisions, this post walks through Vite 3's core changes and what we learned migrating to it.
Core Improvements
Faster Cold Start
Vite 3's dev server now cold-starts noticeably faster than Vite 2. The main optimizations:
# Vite 3 使用 esbuild 做依赖预构建,但优化了缓存策略
vite --force # 强制重新预构建(清除缓存)
# 查看预构建分析
DEBUG=vite:deps npx vite
Built-in CSS Modules Support
/* styles.module.css */
.container {
background: #f5f5f5;
}
/* 自动支持 CSS Modules,不需要额外配置 */
// 直接导入
import styles from './styles.module.css';
// 带类型支持(配合 vite-plugin-css-modules-types)
const cls = styles.container;
Build Optimizations
// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
build: {
// Vite 3 支持 CSS code splitting
cssCodeSplit: true,
// 更好的 sourcemap 支持
sourcemap: true,
// rollup 打包配置
rollupOptions: {
output: {
manualChunks: {
vendor: ['react', 'react-dom'],
router: ['react-router-dom'],
},
},
},
},
// Vite 3 新增:开发服务器配置
server: {
// 更快的 HMR
hmr: {
overlay: true,
},
},
});
Plugin Ecosystem
Vite 3's plugin ecosystem has matured. The core plugins we use day to day:
// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import checker from 'vite-plugin-checker';
import { ViteImageOptimizer } from 'vite-plugin-image-optimizer';
import Inspect from 'vite-plugin-inspect';
export default defineConfig({
plugins: [
react(),
// TypeScript 类型检查(不阻塞编译)
checker({
typescript: true,
eslint: {
lintCommand: 'eslint src --ext .ts,.tsx',
},
}),
// 图片压缩
ViteImageOptimizer({
jpeg: { quality: 80 },
png: { quality: 80 },
}),
// 调试插件(查看中间状态)
Inspect(),
],
});
Migrating from Webpack in Practice
// webpack.config.js → vite.config.ts
// Before (Webpack):
// module.exports = {
// resolve: { alias: { '@': path.resolve('src') } },
// module: { rules: [{ test: /\.svg/, use: ['@svgr/webpack'] }] },
// plugins: [new HtmlWebpackPlugin(), new DefinePlugin({...})],
// devServer: { port: 3000, proxy: { '/api': 'http://localhost:8080' } },
// };
// After (Vite):
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import svgr from 'vite-plugin-svgr';
import path from 'path';
export default defineConfig({
resolve: {
alias: { '@': path.resolve(__dirname, 'src') },
},
plugins: [
react(),
svgr(), // SVG 作为 React 组件导入
],
server: {
port: 3000,
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
},
},
},
define: {
'process.env': {},
},
css: {
preprocessorOptions: {
less: {
modifyVars: { '@primary-color': '#1890ff' },
javascriptEnabled: true,
},
},
},
});
Environment Variable Migration
# Webpack: .env 中用 REACT_APP_ 前缀
REACT_APP_API_URL=https://api.example.com
# Vite: 改为 VITE_ 前缀
VITE_API_URL=https://api.example.com
// Before
const url = process.env.REACT_APP_API_URL;
// After
const url = import.meta.env.VITE_API_URL;
Sharing Vite Config in a Monorepo
// packages/vite-config/index.ts
import { defineConfig, type UserConfig } from 'vite';
import react from '@vitejs/plugin-react';
import path from 'path';
export function createViteConfig(
overrides: Partial<UserConfig> = {}
): UserConfig {
return defineConfig({
plugins: [react()],
resolve: {
alias: { '@': path.resolve('src') },
},
build: {
target: 'es2020',
minify: 'esbuild',
},
...overrides,
});
}
// apps/admin/vite.config.ts
import { createViteConfig } from '@mono/vite-config';
export default createViteConfig({
server: { port: 3001 },
build: {
outDir: 'dist/admin',
},
});
Performance Comparison
Here are the real numbers from one of our mid-sized admin projects (200+ page components):
| Metric | Webpack 5 | Vite 2 | Vite 3 |
|---|---|---|---|
| Cold start | 45s | 3.2s | 2.1s |
| HMR | 800ms | 28ms | 18ms |
| Production build | 120s | 35s | 28s |
The cold-start improvement is the most dramatic, and it's a qualitative leap for the developer experience.
Summary
Vite 3 marks a new phase for frontend build tooling. The browser-native-ESM dev model has now been proven viable. For new projects, there's no reason not to use Vite. For legacy projects, the migration cost is low and the payoff is clear. Next up I'll cover Vitest — Vite's native test runner — which rounds out a complete toolchain alongside Vite.
