Vite(法语”快”的意思)是新一代前端构建工具,由 Vue.js 作者尤雨溪开发。本文将深入探讨 Vite 的工作原理。
为什么需要 Vite?
Webpack 的痛点
传统的打包工具(如 Webpack)在项目启动时需要:
- 分析所有模块依赖
- 将代码打包成 bundle
- 启动开发服务器
随着项目规模增大,启动时间可能长达几分钟。
Vite 的解决方案
传统打包器:
源代码 → 打包 → 启动服务器 → 浏览器访问
(慢)
Vite:
源代码 → 启动服务器 → 浏览器按需请求 → 即时编译
(快) (快)
核心原理
1. 利用原生 ES Modules
现代浏览器原生支持 ES Modules:
<!-- 浏览器直接执行 -->
<script type="module">
import { createApp } from '/node_modules/.vite/deps/vue.js'
import App from '/src/App.vue'
createApp(App).mount('#app')
</script>
2. 依赖预构建 (Pre-bundling)
Vite 使用 esbuild 预构建 node_modules 中的依赖:
// 预构建的作用:
// 1. 将 CommonJS/UMD 转换为 ESM
// 2. 合并小模块,减少 HTTP 请求
// lodash-es 有 600+ 个模块
import { debounce } from 'lodash-es'
// 预构建后只需一个请求
预构建结果缓存在 node_modules/.vite 目录。
3. 按需编译
浏览器请求 → Vite 服务器 → 即时编译 → 返回
/src/App.vue ↓
编译 Vue SFC
↓
返回 JS 代码
开发服务器原理
// Vite 开发服务器简化原理
import { createServer } from 'vite'
const server = await createServer({
root: process.cwd(),
server: { port: 5173 }
})
// 核心中间件
server.middlewares.use(async (req, res, next) => {
const url = req.url
if (url.endsWith('.vue')) {
// 编译 Vue 文件
const result = await compileSFC(url)
res.setHeader('Content-Type', 'application/javascript')
res.end(result)
} else if (url.endsWith('.ts')) {
// 编译 TypeScript
const result = await esbuild.transform(code, { loader: 'ts' })
res.end(result.code)
} else {
next()
}
})
HMR (热模块替换)
HMR API
// src/counter.js
export let count = 0
export function increment() {
count++
render()
}
// HMR 支持
if (import.meta.hot) {
import.meta.hot.accept((newModule) => {
// 模块更新时的处理
count = newModule.count
render()
})
}
HMR 流程
文件修改 → Vite 服务器检测 → WebSocket 通知浏览器 → 浏览器请求新模块 → 热替换
配置详解
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'),
'@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,
rollupOptions: {
output: {
manualChunks: {
vendor: ['vue', 'vue-router'],
utils: ['lodash-es', 'dayjs'],
},
},
},
},
// CSS 配置
css: {
preprocessorOptions: {
scss: {
additionalData: `@import "@/styles/variables.scss";`,
},
},
},
// 环境变量
envPrefix: 'APP_',
})
环境变量
# .env.development
VITE_API_URL=http://localhost:8080
VITE_DEBUG=true
# .env.production
VITE_API_URL=https://api.example.com
VITE_DEBUG=false
// 使用环境变量
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
插件系统
插件钩子
// my-vite-plugin.js
export default function myPlugin() {
return {
name: 'my-plugin',
// 配置解析阶段
config(config, { command }) {
if (command === 'build') {
config.base = '/my-app/'
}
},
// 服务器配置
configureServer(server) {
server.middlewares.use((req, res, next) => {
// 自定义中间件
next()
})
},
// 模块转换
transform(code, id) {
if (id.endsWith('.md')) {
return transformMarkdown(code)
}
},
// 构建阶段钩子
buildStart() {
console.log('构建开始')
},
buildEnd() {
console.log('构建结束')
},
}
}
常用插件
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { compression } from 'vite-plugin-compression2'
export default defineConfig({
plugins: [
vue(),
// 自动导入 API
AutoImport({
imports: ['vue', 'vue-router'],
dts: 'src/auto-imports.d.ts',
}),
// 自动导入组件
Components({
dirs: ['src/components'],
dts: 'src/components.d.ts',
}),
// Gzip 压缩
compression({
algorithm: 'gzip',
}),
],
})
性能优化
1. 依赖优化
export default defineConfig({
optimizeDeps: {
// 强制预构建
include: ['lodash-es', 'axios'],
// 排除预构建
exclude: ['your-local-package'],
},
})
2. 代码分割
export default defineConfig({
build: {
rollupOptions: {
output: {
manualChunks(id) {
if (id.includes('node_modules')) {
// 将 node_modules 按包名分割
const name = id.split('node_modules/')[1].split('/')[0]
return `vendor-${name}`
}
},
},
},
},
})
3. 构建分析
# 安装分析插件
npm i -D rollup-plugin-visualizer
# 配置
import { visualizer } from 'rollup-plugin-visualizer'
export default defineConfig({
plugins: [
visualizer({
open: true,
filename: 'stats.html',
}),
],
})
Vite vs Webpack
| 特性 | Vite | Webpack |
|---|---|---|
| 开发启动 | 毫秒级 | 秒/分钟级 |
| HMR 速度 | 极快 | 较慢 |
| 配置复杂度 | 简单 | 复杂 |
| 生态系统 | 成长中 | 成熟 |
| 浏览器兼容 | 现代浏览器 | 可配置 |
| 生产构建 | Rollup | Webpack |
迁移指南
从 Vue CLI 迁移
# 1. 安装 Vite
npm install vite @vitejs/plugin-vue -D
# 2. 创建 vite.config.ts
# 3. 更新 index.html (移到根目录)
# 4. 更新 package.json scripts
{
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
}
}
# 5. 删除 vue.config.js
index.html 调整
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>My App</title>
</head>
<body>
<div id="app"></div>
<!-- 入口脚本必须使用 type="module" -->
<script type="module" src="/src/main.ts"></script>
</body>
</html>
总结
Vite 的核心优势:
- 极速启动 - 利用原生 ESM,无需打包
- 即时 HMR - 模块级别的热更新
- 开箱即用 - TypeScript、JSX、CSS 预处理器支持
- 灵活配置 - 基于 Rollup 的插件系统
- 优化构建 - 生产环境使用 Rollup 打包
Vite 代表了前端构建工具的未来方向,值得每个前端开发者学习掌握。