📖 文章概述
Vite 5 是下一代前端构建工具,以其闪电般的开发服务器速度和最优化的生产构建闻名。本文详解 Vite 5 的核心优化技巧、插件开发和实战应用。
🎯 Vite 核心优势
Vite vs Webpack 对比
| 特性 | Webpack | Vite |
|---|---|---|
| 启动时间 | 30-60s | 300-500ms |
| HMR 更新 | 3-10s | 50-100ms |
| 内存占用 | 500-1000MB | 100-200MB |
| 学习曲线 | 陡峭 | 平缓 |
| 生产优化 | ✅ | ✅✅ |
为什么 Vite 这么快?
// 1. 利用原生 ES 模块(ESM)
// 浏览器原生支持 import,无需打包即可加载
// 2. 按需编译
// Vite 只编译正在访问的模块
// 3. 缓存优化
// 充分利用浏览器缓存和 HTTP 头
// 4. esbuild 驱动
// 使用 Rust 编写的超快速打包器
🚀 项目配置优化
1. 基础 vite.config.ts 配置
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'),
'~': path.resolve(__dirname, './'),
}
},
// 开发服务器配置
server: {
port: 3000,
open: true,
proxy: {
'/api': {
target: 'http://localhost:3001',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '')
}
},
// 预热文件,加速冷启动
warmup: {
clientFiles: ['./src/**/*.{js,ts,vue}']
}
},
// 预览配置
preview: {
port: 5000,
strictPort: false
}
})
2. 构建优化配置
export default defineConfig({
build: {
// 输出目录
outDir: 'dist',
// 清空输出目录
emptyOutDir: true,
// 生成源映射(生产环境建议关闭)
sourcemap: false,
// 代码分割配置
rollupOptions: {
output: {
// 按类型分组
manualChunks: {
'vue': ['vue'],
'vue-router': ['vue-router'],
'vendor': ['axios', 'lodash']
},
// 自定义文件输出名
entryFileNames: 'js/[name]-[hash].js',
chunkFileNames: 'js/[name]-[hash].js',
assetFileNames: ({name}) => {
if (/\.(gif|jpe?g|png|svg)$/.test(name ?? '')) {
return 'images/[name]-[hash][extname]'
}
if (/\.css$/.test(name ?? '')) {
return 'css/[name]-[hash][extname]'
}
return 'assets/[name]-[hash][extname]'
}
}
},
// 压缩配置
minify: 'terser',
terserOptions: {
compress: {
drop_console: true, // 删除 console
drop_debugger: true // 删除 debugger
}
},
// 报告压缩后的包大小
reportCompressedSize: true,
// 分割块大小限制
chunkSizeWarningLimit: 1000
}
})
3. 库模式配置
export default defineConfig({
build: {
lib: {
entry: path.resolve(__dirname, 'src/index.ts'),
name: 'MyLibrary',
fileName: (format) => `my-lib.${format}.js`
},
rollupOptions: {
// 不要将这些打包进库
external: ['vue'],
output: {
globals: {
vue: 'Vue'
}
}
}
}
})
🔌 插件系统开发
4. 编写自定义 Vite 插件
// 插件结构
import type { Plugin } from 'vite'
export function myPlugin(options = {}): Plugin {
return {
name: 'my-plugin', // 必需,用于警告和错误报告
// 通用钩子
apply: 'both', // 'serve' | 'build' | 'both'
// Vite 特定钩子
config(config, env) {
// 修改 Vite 配置
if (env.command === 'serve') {
return {
define: {
__DEV__: true
}
}
}
},
configResolved(resolvedConfig) {
// 保存配置引用
},
configureServer(server) {
// 配置开发服务器中间件
return () => {
server.middlewares.use('/custom', (req, res) => {
res.end('Custom response')
})
}
},
transformIndexHtml(html) {
// 转换 index.html 内容
return html.replace(
/<title>(.*?)<\/title>/,
'<title>Modified Title</title>'
)
},
resolveId(id) {
// 自定义模块解析
if (id === 'virtual-module') {
return id
}
},
load(id) {
// 加载虚拟模块
if (id === 'virtual-module') {
return `export const msg = "from virtual module"`
}
},
transform(code, id) {
// 转换模块代码
if (id.endsWith('.custom')) {
return {
code: transformCode(code),
map: null
}
}
},
handleHotUpdate({ file, server, modules }) {
// 自定义 HMR 处理
if (file.endsWith('.custom')) {
console.log(`Custom file updated: ${file}`)
server.ws.send({
type: 'custom',
event: 'special',
data: { file }
})
return []
}
}
}
}
5. 实战:环境变量插件
// plugins/env-plugin.ts
import type { Plugin } from 'vite'
import fs from 'fs'
import path from 'path'
interface EnvConfig {
[key: string]: any
}
export function envPlugin(envPath: string): Plugin {
return {
name: 'env-plugin',
apply: 'serve',
config() {
const envFile = path.resolve(envPath, '.env.development')
let envConfig: EnvConfig = {}
if (fs.existsSync(envFile)) {
const content = fs.readFileSync(envFile, 'utf-8')
envConfig = parseEnv(content)
}
return {
define: {
__ENV__: JSON.stringify(envConfig)
}
}
},
handleHotUpdate({ file, server }) {
if (file.endsWith('.env.development')) {
// .env 文件变化时刷新页面
server.ws.send({
type: 'full',
event: 'full-reload'
})
return []
}
}
}
}
function parseEnv(content: string): EnvConfig {
const config: EnvConfig = {}
content.split('\n').forEach(line => {
const [key, value] = line.split('=')
if (key && value) {
config[key.trim()] = value.trim()
}
})
return config
}
6. 虚拟模块插件
// plugins/virtual-module.ts
import type { Plugin } from 'vite'
const virtualModuleId = 'virtual:my-module'
const resolvedId = '\0' + virtualModuleId
export function virtualModulePlugin(): Plugin {
return {
name: 'virtual-module',
resolveId(id) {
if (id === virtualModuleId) {
return resolvedId
}
},
load(id) {
if (id === resolvedId) {
return `
export const greeting = 'Hello from virtual module'
export const timestamp = ${Date.now()}
`
}
}
}
}
// 使用
import { greeting } from 'virtual:my-module'
console.log(greeting)
⚡ 性能监控与分析
7. 构建分析
import { visualizer } from 'rollup-plugin-visualizer'
export default defineConfig({
plugins: [
visualizer({
open: true,
gzipSize: true,
brotliSize: true,
filename: 'dist/stats.html'
})
]
})
8. 开发服务器性能监控
// vite.config.ts
export default defineConfig({
server: {
middlewareMode: false,
// 检测缓慢的模块
middlewares: {
// 自定义中间件记录加载时间
shouldLogRequest() {
return true
}
}
}
})
// 在代码中监控
if (import.meta.hot) {
import.meta.hot.on('vite:beforeFullReload', () => {
console.log('Full reload triggered')
})
import.meta.hot.on('vite:beforePrune', ({ paths }) => {
console.log('Pruning', paths)
})
}
🔄 实战:完整优化方案
完整生产级 vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import path from 'path'
import compression from 'vite-plugin-compression'
export default defineConfig({
plugins: [
vue(),
// Gzip 压缩
compression({
verbose: true,
disable: false,
threshold: 10240,
algorithm: 'gzip',
ext: '.gz'
})
],
resolve: {
alias: {
'@': path.resolve(__dirname, './src')
}
},
server: {
port: 3000,
proxy: {
'/api': 'http://localhost:3001'
},
warmup: {
clientFiles: ['./src/main.ts', './src/App.vue']
}
},
build: {
outDir: 'dist',
sourcemap: false,
minify: 'terser',
rollupOptions: {
output: {
manualChunks: {
'vue': ['vue'],
'vue-router': ['vue-router'],
'pinia': ['pinia'],
'vendor': ['axios', 'lodash', 'date-fns']
}
}
},
chunkSizeWarningLimit: 1500,
terserOptions: {
compress: {
drop_console: true,
drop_debugger: true
}
}
},
// CSS 优化
css: {
postcss: {
plugins: [
require('autoprefixer'),
require('cssnano')
]
},
preprocessorOptions: {
scss: {
additionalData: `@import "@/styles/variables.scss";`
}
}
}
})
🐛 常见问题解决
问题 1:HMR 无法工作
// ❌ 问题:生产环境配置不当
export default defineConfig({
server: {
hmr: true // 默认自动检测
}
})
// ✅ 解决:显式配置
export default defineConfig({
server: {
hmr: {
host: 'localhost',
port: 3000,
protocol: 'ws' // 或 'wss'
}
}
})
问题 2:懒加载模块加载缓慢
// ❌ 问题:没有预热关键路由
export default defineConfig({
server: {
warmup: {
clientFiles: [] // 空
}
}
})
// ✅ 解决:预热常用模块
export default defineConfig({
server: {
warmup: {
clientFiles: [
'./src/main.ts',
'./src/App.vue',
'./src/pages/Home.vue'
]
}
}
})
问题 3:CSS 预处理器慢
// ❌ 问题:没有缓存
export default defineConfig({
css: {
preprocessorOptions: {
scss: {}
}
}
})
// ✅ 解决:启用 Dart Sass(更快)
// npm install -D sass
export default defineConfig({
css: {
preprocessorOptions: {
scss: {
implementation: 'sass'
}
}
}
})
📊 优化效果对比
性能指标提升
| 指标 | 优化前 | 优化后 | 提升 |
|---|---|---|---|
| 启动时间 | 5.2s | 0.8s | ⬇️ 85% |
| HMR 反应时间 | 2.1s | 0.15s | ⬇️ 93% |
| 首屏加载时间 | 3.5s | 1.2s | ⬇️ 66% |
| 包体积 | 850KB | 320KB | ⬇️ 62% |
🎓 最佳实践
DO ✅
// 1. 合理分割代码
manualChunks: {
'vue': ['vue'],
'router': ['vue-router'],
'ui': ['element-plus']
}
// 2. 预热关键模块
warmup: {
clientFiles: ['./src/main.ts', './src/App.vue']
}
// 3. 监控包大小
build: {
reportCompressedSize: true,
chunkSizeWarningLimit: 1500
}
// 4. 环境区分
export default ({ command }) => {
if (command === 'serve') {
return { ... }
} else {
return { ... }
}
}
DON'T ❌
// 1. 过度分割
manualChunks: {
'module-a': ['./src/modules/a.js'],
'module-b': ['./src/modules/b.js'],
// ... 太多小块
}
// 2. 所有文件都启用 sourcemap
build: {
sourcemap: true // 生产环境会增加体积
}
// 3. 禁用压缩
build: {
minify: false
}
// 4. 同步操作阻塞主线程
transform(code, id) {
// ❌ 不要使用 fs.readFileSync
const file = fs.readFileSync('somefile')
}
📚 扩展资源
总结
Vite 5 通过以下机制实现超快速开发体验:
- 原生 ESM:充分利用浏览器能力
- 按需编译:只编译必要的代码
- 智能缓存:最大化复用率
- 插件系统:灵活扩展功能
掌握这些优化技巧,能够显著提升开发效率和用户体验!