Vite 构建工具
Vite 是一个现代化的前端构建工具,为 Vue.js 项目提供了极快的开发体验和优化的生产构建。
什么是 Vite?
Vite(法语意为"快速")是一个构建工具,具有以下特点:
- ⚡️ 极速的服务启动
- 🔥 闪电般的热模块替换 (HMR)
- 🛠️ 丰富的功能
- 📦 优化的构建
- 🔩 通用的插件接口
- 🔑 完全类型化的 API
快速开始
创建新项目
bash
# npm
npm create vue@latest my-vue-app
# yarn
yarn create vue my-vue-app
# pnpm
pnpm create vue my-vue-app
# 使用 Vite 模板
npm create vite@latest my-vue-app -- --template vue
项目结构
my-vue-app/
├── public/
│ └── favicon.ico
├── src/
│ ├── assets/
│ ├── components/
│ ├── App.vue
│ └── main.js
├── index.html
├── package.json
├── vite.config.js
└── README.md
基本配置
javascript
// vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { resolve } from 'path'
export default defineConfig({
plugins: [vue()],
// 开发服务器配置
server: {
port: 3000,
open: true,
cors: true,
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '')
}
}
},
// 构建配置
build: {
outDir: 'dist',
assetsDir: 'assets',
sourcemap: false,
minify: 'terser',
rollupOptions: {
output: {
chunkFileNames: 'js/[name]-[hash].js',
entryFileNames: 'js/[name]-[hash].js',
assetFileNames: '[ext]/[name]-[hash].[ext]'
}
}
},
// 路径别名
resolve: {
alias: {
'@': resolve(__dirname, 'src'),
'@components': resolve(__dirname, 'src/components'),
'@assets': resolve(__dirname, 'src/assets'),
'@utils': resolve(__dirname, 'src/utils')
}
},
// CSS 配置
css: {
preprocessorOptions: {
scss: {
additionalData: `@import "@/styles/variables.scss";`
}
}
},
// 环境变量
define: {
__APP_VERSION__: JSON.stringify(process.env.npm_package_version)
}
})
开发功能
热模块替换 (HMR)
Vite 提供了开箱即用的 HMR 支持:
vue
<!-- 组件会自动支持 HMR -->
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<button @click="count++">Count: {{ count }}</button>
</div>
</template>
<script setup>
import { ref } from 'vue'
defineProps({
msg: String
})
const count = ref(0)
// HMR API(通常不需要手动使用)
if (import.meta.hot) {
import.meta.hot.accept()
}
</script>
<style scoped>
.hello {
color: #42b883;
}
</style>
环境变量
javascript
// .env
VITE_APP_TITLE=My Vue App
VITE_API_BASE_URL=https://api.example.com
// .env.development
VITE_API_BASE_URL=http://localhost:3000/api
// .env.production
VITE_API_BASE_URL=https://prod-api.example.com
在代码中使用:
javascript
// main.js
console.log(import.meta.env.VITE_APP_TITLE)
console.log(import.meta.env.VITE_API_BASE_URL)
console.log(import.meta.env.MODE) // 'development' 或 'production'
console.log(import.meta.env.DEV) // boolean
console.log(import.meta.env.PROD) // boolean
// 在组件中
export default {
setup() {
const apiUrl = import.meta.env.VITE_API_BASE_URL
return {
apiUrl
}
}
}
静态资源处理
vue
<template>
<div>
<!-- public 目录下的文件 -->
<img src="/logo.png" alt="Logo">
<!-- src/assets 目录下的文件 -->
<img :src="logoUrl" alt="Logo">
<!-- 内联导入 -->
<img :src="inlineLogo" alt="Logo">
<!-- 显式 URL 导入 -->
<img :src="explicitUrl" alt="Logo">
</div>
</template>
<script setup>
// 默认导入(会被处理和优化)
import logoUrl from '@/assets/logo.png'
// 内联导入(转为 base64)
import inlineLogo from '@/assets/logo.png?inline'
// 显式 URL 导入
import explicitUrl from '@/assets/logo.png?url'
// 动态导入
import { ref, onMounted } from 'vue'
const dynamicImage = ref('')
onMounted(async () => {
const module = await import('@/assets/dynamic.png')
dynamicImage.value = module.default
})
</script>
插件系统
常用插件
javascript
// vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { resolve } from 'path'
// 自动导入
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
// PWA
import { VitePWA } from 'vite-plugin-pwa'
// 图标
import Icons from 'unplugin-icons/vite'
import IconsResolver from 'unplugin-icons/resolver'
// ESLint
import eslint from 'vite-plugin-eslint'
export default defineConfig({
plugins: [
vue(),
// ESLint 集成
eslint({
include: ['src/**/*.js', 'src/**/*.vue', 'src/*.js', 'src/*.vue']
}),
// 自动导入 Vue API
AutoImport({
imports: ['vue', 'vue-router', 'pinia'],
resolvers: [
ElementPlusResolver(),
IconsResolver({ prefix: 'Icon' })
],
dts: true // 生成类型定义文件
}),
// 自动导入组件
Components({
resolvers: [
ElementPlusResolver(),
IconsResolver({ enabledCollections: ['ep'] })
],
dts: true
}),
// 图标支持
Icons({
autoInstall: true
}),
// PWA 支持
VitePWA({
registerType: 'autoUpdate',
workbox: {
globPatterns: ['**/*.{js,css,html,ico,png,svg}']
},
manifest: {
name: 'My Vue App',
short_name: 'VueApp',
description: 'My Awesome Vue App',
theme_color: '#ffffff',
icons: [
{
src: 'pwa-192x192.png',
sizes: '192x192',
type: 'image/png'
}
]
}
})
]
})
自定义插件
javascript
// plugins/virtual-module.js
export function virtualModulePlugin() {
const virtualModuleId = 'virtual:my-module'
const resolvedVirtualModuleId = '\0' + virtualModuleId
return {
name: 'virtual-module',
resolveId(id) {
if (id === virtualModuleId) {
return resolvedVirtualModuleId
}
},
load(id) {
if (id === resolvedVirtualModuleId) {
return `export const msg = "Hello from virtual module!"`
}
}
}
}
// 使用插件
import { virtualModulePlugin } from './plugins/virtual-module'
export default defineConfig({
plugins: [
vue(),
virtualModulePlugin()
]
})
// 在代码中使用虚拟模块
import { msg } from 'virtual:my-module'
console.log(msg) // "Hello from virtual module!"
构建优化
代码分割
javascript
// vite.config.js
export default defineConfig({
build: {
rollupOptions: {
output: {
manualChunks: {
// 将 Vue 相关库打包到一个 chunk
vue: ['vue', 'vue-router', 'pinia'],
// 将 UI 库打包到一个 chunk
ui: ['element-plus'],
// 将工具库打包到一个 chunk
utils: ['lodash', 'axios', 'dayjs']
}
}
}
}
})
// 或者使用函数形式
export default defineConfig({
build: {
rollupOptions: {
output: {
manualChunks(id) {
// 将 node_modules 中的包分组
if (id.includes('node_modules')) {
if (id.includes('vue')) {
return 'vue'
}
if (id.includes('element-plus')) {
return 'ui'
}
return 'vendor'
}
}
}
}
}
})
动态导入
javascript
// 路由懒加载
const routes = [
{
path: '/home',
component: () => import('@/views/Home.vue')
},
{
path: '/about',
component: () => import('@/views/About.vue')
}
]
// 组件懒加载
export default {
components: {
AsyncComponent: () => import('@/components/AsyncComponent.vue')
}
}
// 条件导入
async function loadFeature() {
if (someCondition) {
const { feature } = await import('@/features/advanced')
return feature
}
}
预加载和预获取
javascript
// 预加载关键资源
import { preloadModule } from 'vite/dynamic-import-polyfill'
// 预加载下一个可能访问的页面
function preloadNextPage() {
preloadModule(() => import('@/views/NextPage.vue'))
}
// 在路由守卫中预加载
router.beforeEach((to, from, next) => {
// 预加载目标路由组件
if (to.matched.some(record => record.meta.preload)) {
import(to.matched[0].component)
}
next()
})
多环境配置
环境特定配置
javascript
// vite.config.js
import { defineConfig, loadEnv } from 'vite'
export default defineConfig(({ command, mode }) => {
// 加载环境变量
const env = loadEnv(mode, process.cwd(), '')
return {
plugins: [vue()],
// 根据环境调整配置
server: {
port: env.VITE_PORT || 3000
},
build: {
// 开发环境生成 sourcemap
sourcemap: command === 'serve',
// 生产环境压缩
minify: mode === 'production' ? 'terser' : false
},
define: {
__APP_ENV__: JSON.stringify(mode),
__API_URL__: JSON.stringify(env.VITE_API_URL)
}
}
})
多个配置文件
javascript
// vite.config.base.js
export const baseConfig = {
plugins: [vue()],
resolve: {
alias: {
'@': resolve(__dirname, 'src')
}
}
}
// vite.config.dev.js
import { defineConfig } from 'vite'
import { baseConfig } from './vite.config.base'
export default defineConfig({
...baseConfig,
server: {
port: 3000,
open: true
}
})
// vite.config.prod.js
import { defineConfig } from 'vite'
import { baseConfig } from './vite.config.base'
export default defineConfig({
...baseConfig,
build: {
minify: 'terser',
rollupOptions: {
// 生产环境特定配置
}
}
})
性能优化
依赖预构建
javascript
// vite.config.js
export default defineConfig({
optimizeDeps: {
// 强制预构建
include: ['lodash', 'axios'],
// 排除预构建
exclude: ['your-local-package'],
// 自定义 esbuild 选项
esbuildOptions: {
target: 'es2020'
}
}
})
构建分析
javascript
// 安装分析插件
npm install --save-dev rollup-plugin-visualizer
// vite.config.js
import { visualizer } from 'rollup-plugin-visualizer'
export default defineConfig({
plugins: [
vue(),
// 构建分析
visualizer({
filename: 'dist/stats.html',
open: true,
gzipSize: true,
brotliSize: true
})
]
})
缓存策略
javascript
// vite.config.js
export default defineConfig({
build: {
rollupOptions: {
output: {
// 文件名包含 hash,利用浏览器缓存
chunkFileNames: 'js/[name]-[hash].js',
entryFileNames: 'js/[name]-[hash].js',
assetFileNames: (assetInfo) => {
const info = assetInfo.name.split('.')
const ext = info[info.length - 1]
if (/\.(png|jpe?g|gif|svg|webp)$/i.test(assetInfo.name)) {
return `images/[name]-[hash].${ext}`
}
if (/\.(css)$/i.test(assetInfo.name)) {
return `css/[name]-[hash].${ext}`
}
return `assets/[name]-[hash].${ext}`
}
}
}
}
})
调试和开发工具
Source Maps
javascript
// vite.config.js
export default defineConfig({
build: {
sourcemap: true, // 或 'inline', 'hidden'
},
css: {
devSourcemap: true // CSS source maps
}
})
开发服务器配置
javascript
// vite.config.js
export default defineConfig({
server: {
// 自定义端口
port: 3000,
// 自动打开浏览器
open: true,
// 启用 HTTPS
https: true,
// 代理配置
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '')
},
// WebSocket 代理
'/ws': {
target: 'ws://localhost:8080',
ws: true
}
},
// 自定义中间件
middlewareMode: false,
// 文件监听配置
watch: {
usePolling: true,
interval: 1000
}
}
})
部署
静态部署
bash
# 构建
npm run build
# 预览构建结果
npm run preview
Nginx 配置
nginx
server {
listen 80;
server_name example.com;
root /var/www/dist;
index index.html;
# 处理 SPA 路由
location / {
try_files $uri $uri/ /index.html;
}
# 静态资源缓存
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# Gzip 压缩
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
}
Docker 部署
dockerfile
# Dockerfile
FROM node:18-alpine as build-stage
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
# 生产阶段
FROM nginx:stable-alpine as production-stage
COPY --from=build-stage /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/nginx.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
故障排除
常见问题
javascript
// 1. 依赖预构建问题
// 删除 node_modules/.vite 目录
rm -rf node_modules/.vite
// 2. 端口冲突
// vite.config.js
export default defineConfig({
server: {
port: 3001 // 更改端口
}
})
// 3. 路径别名不生效
// 确保 jsconfig.json 或 tsconfig.json 配置正确
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
}
}
}
// 4. 环境变量不生效
// 确保变量名以 VITE_ 开头
VITE_API_URL=http://localhost:3000
// 5. 热更新不工作
// 检查文件监听配置
export default defineConfig({
server: {
watch: {
usePolling: true
}
}
})
调试配置
javascript
// vite.config.js
export default defineConfig({
// 启用详细日志
logLevel: 'info',
// 清除控制台
clearScreen: false,
// 自定义日志
customLogger: {
info(msg) {
console.log(`[INFO] ${msg}`)
},
warn(msg) {
console.warn(`[WARN] ${msg}`)
},
error(msg) {
console.error(`[ERROR] ${msg}`)
}
}
})
最佳实践
1. 项目结构
src/
├── assets/ # 静态资源
├── components/ # 公共组件
├── composables/ # 组合式函数
├── layouts/ # 布局组件
├── pages/ # 页面组件
├── router/ # 路由配置
├── stores/ # 状态管理
├── styles/ # 全局样式
├── utils/ # 工具函数
├── App.vue
└── main.js
2. 配置管理
javascript
// 使用配置工厂函数
export function createViteConfig(options = {}) {
const { mode = 'development', plugins = [] } = options
return defineConfig({
plugins: [vue(), ...plugins],
server: {
port: mode === 'development' ? 3000 : 4000
},
build: {
sourcemap: mode === 'development'
}
})
}
3. 性能监控
javascript
// 添加构建时间监控
const startTime = Date.now()
export default defineConfig({
plugins: [
vue(),
{
name: 'build-time',
buildEnd() {
console.log(`Build completed in ${Date.now() - startTime}ms`)
}
}
]
})
下一步
Vite 为 Vue 开发提供了现代化的工具链,掌握它将大大提升你的开发效率!