Vite 初级指南
Vite 是一个现代前端构建工具,由 Vue 作者尤雨溪创建。它的核心设计目标是解决传统打包工具(如 Webpack)在大型项目中开发体验缓慢的问题。 Webpack 的工作方式是在启动开发服务器前,先把所有模块打包成 bundle,然后再提供服务。随着项目规模增大,这个打包过程会越来越慢,冷启动时间可能长达数十秒甚至数分钟。 Vite 采用了完全不同的策略: Vite 在不同阶段使用不同策略: 这种分离设计保证了开发体验极速,同时生产产物质量有保障。 Vite 5+ 要求 Node.js 版本: 建议使用 LTS 版本。可用 node -v 检查当前版
官方文档:https://cn.vitejs.dev/
适用版本:Vite 5.x(2026-05-07 核实)
1. Vite 是什么
Vite 是一个现代前端构建工具,由 Vue 作者尤雨溪创建。它的核心设计目标是解决传统打包工具(如 Webpack)在大型项目中开发体验缓慢的问题。
和 Webpack 的对比:为什么更快
Webpack 的工作方式是在启动开发服务器前,先把所有模块打包成 bundle,然后再提供服务。随着项目规模增大,这个打包过程会越来越慢,冷启动时间可能长达数十秒甚至数分钟。
Vite 采用了完全不同的策略:
- 开发阶段:利用浏览器原生 ES Module(ESM)支持,直接按需提供源文件,不需要提前打包。浏览器请求哪个模块,Vite 就处理哪个模块,启动时间极短(通常不超过 1 秒)。
- 依赖预构建:第三方库(node_modules)用 esbuild 提前处理,esbuild 用 Go 编写,比 JavaScript 工具快 10-100 倍。
- 热更新(HMR):修改文件后只更新变化的模块,而不是重新打包整个应用,速度极快且不随项目体积增大而变慢。
| 维度 | Webpack | Vite |
|---|---|---|
| 开发启动 | 打包所有模块后启动 | 直接启动,按需编译 |
| 热更新速度 | 随项目变大变慢 | 始终极快(模块级别) |
| 底层工具 | JavaScript | esbuild(Go)+ Rollup |
| 生产构建 | Webpack 打包 | Rollup 打包 |
| 配置复杂度 | 较高 | 较低,约定优于配置 |
开发服务器 vs 生产构建
Vite 在不同阶段使用不同策略:
- 开发服务器:基于原生 ESM,浏览器直接处理模块图,Vite 只做必要的转换(如 TypeScript 编译、JSX 转换)
- 生产构建:使用 Rollup 打包,因为 Rollup 的 tree-shaking 和代码分割能力更成熟,产物更优化
这种分离设计保证了开发体验极速,同时生产产物质量有保障。
适用场景
- 单页应用(SPA):Vue、React、Svelte 等框架项目
- 多页应用(MPA):多个独立 HTML 入口
- 库开发:构建可发布的 JavaScript 库
- 原型快速搭建:极低的启动成本
- 不适合:需要兼容 IE11 或极老旧浏览器的场景(可用 @vitejs/plugin-legacy 补救,但有性能代价)
2. 安装与创建项目
Node.js 版本要求
Vite 5+ 要求 Node.js 版本:
18.x(18.0.0+)20.x(20.19.0+)22.x(22.12.0+)
建议使用 LTS 版本。可用 node -v 检查当前版本。
使用 npm create vite@latest 创建项目
这是最推荐的方式,会交互式引导选择框架和语言:
npm create vite@latest
也可以直接指定项目名和模板,跳过交互:
# 格式:npm create vite@latest <项目名> -- --template <模板名>
npm create vite@latest my-app -- --template vue
npm create vite@latest my-app -- --template react-ts
等价的其他包管理器写法:
# yarn
yarn create vite my-app --template vue
# pnpm
pnpm create vite my-app --template react
# bun
bun create vite my-app --template svelte
官方模板列表
| 模板名 | 框架 | 语言 |
|---|---|---|
vanilla |
无框架 | JavaScript |
vanilla-ts |
无框架 | TypeScript |
vue |
Vue 3 | JavaScript |
vue-ts |
Vue 3 | TypeScript |
react |
React | JavaScript |
react-ts |
React | TypeScript |
react-swc |
React(SWC 编译) | JavaScript |
react-swc-ts |
React(SWC 编译) | TypeScript |
preact |
Preact | JavaScript |
preact-ts |
Preact | TypeScript |
lit |
Lit(Web Components) | JavaScript |
lit-ts |
Lit | TypeScript |
svelte |
Svelte | JavaScript |
svelte-ts |
Svelte | TypeScript |
solid |
Solid.js | JavaScript |
solid-ts |
Solid.js | TypeScript |
qwik |
Qwik | JavaScript |
qwik-ts |
Qwik | TypeScript |
手动安装方式
如果需要将 Vite 加入已有项目:
# 安装 Vite
npm install -D vite
# 若使用 Vue,还需要安装插件
npm install -D @vitejs/plugin-vue
# 若使用 React,还需要安装插件
npm install -D @vitejs/plugin-react
然后在 package.json 中添加脚本:
{
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
}
}
并在项目根目录创建 index.html 作为入口(详见第 3 节)。
创建项目后的步骤
cd my-app
npm install
npm run dev
3. 项目结构
典型目录结构
my-project/
├── public/ # 静态资源(不经过 Vite 处理,直接复制到 dist/)
│ └── favicon.ico
├── src/
│ ├── assets/ # 经过 Vite 处理的资源(图片、字体等)
│ ├── components/ # 组件
│ ├── main.js # JavaScript 入口文件
│ └── App.vue # 根组件(Vue 示例)
├── index.html # HTML 入口(Vite 项目核心)
├── vite.config.js # Vite 配置文件
├── package.json
└── .env # 环境变量文件
index.html 为什么是核心
在 Webpack 项目中,index.html 通常放在 public/ 目录,是一个模板文件,由 html-webpack-plugin 注入打包后的 bundle 路径。真正的入口是 webpack.config.js 里指定的 JS 文件。
Vite 的设计截然不同:index.html 就是项目真正的入口,放在项目根目录。Vite 把 index.html 视为模块图的一部分,直接解析其中的 <script type="module"> 标签,从而找到 JS 入口:
<!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>
<!-- Vite 从这里找到入口,不需要额外配置 -->
<script type="module" src="/src/main.js"></script>
</body>
</html>
这个设计带来的好处:
- HTML 文件中可以直接使用
<link>和<script>引用资源,Vite 会自动处理 - 多页应用只需要多个 HTML 文件,不需要复杂配置
- 开发和生产环境的 HTML 处理方式一致
public 目录 vs src/assets 目录
| 特性 | public/ |
src/assets/ |
|---|---|---|
| 处理方式 | 直接复制,不做任何处理 | 经过 Vite 处理(hash 文件名、压缩等) |
| 引用方式 | 用绝对路径 /favicon.ico |
用 import 语句或相对路径 |
| 适合内容 | 不需要处理的文件,如 robots.txt、已压缩的库 | 需要版本化管理的图片、字体、CSS |
| 缓存控制 | 无 hash,靠服务器配置缓存 | 有内容 hash,可长期缓存 |
4. vite.config.js 基础配置
完整配置示例
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { resolve } from 'path'
export default defineConfig({
// 项目根目录(index.html 所在位置)
root: process.cwd(),
// 部署的基础路径,部署到子路径时修改此项
base: '/',
// 静态资源目录
publicDir: 'public',
// 插件列表
plugins: [vue()],
// 模块解析选项
resolve: {
alias: {
'@': resolve(__dirname, 'src'),
},
extensions: ['.mjs', '.js', '.mts', '.ts', '.jsx', '.tsx', '.json'],
},
// 全局常量替换
define: {
__APP_VERSION__: JSON.stringify('1.0.0'),
},
// 环境变量文件目录
envDir: '.',
// 暴露给客户端的环境变量前缀
envPrefix: 'VITE_',
// 日志级别
logLevel: 'info',
// 启动时是否清屏
clearScreen: true,
// 应用类型
appType: 'spa',
// 开发服务器配置
server: {
port: 5173,
open: true,
},
// 构建配置
build: {
outDir: 'dist',
minify: 'esbuild',
},
})
顶级参数说明
| 参数 | 类型 | 默认值 | 是否必填 | 说明 |
|---|---|---|---|---|
root |
string |
process.cwd() |
否 | 项目根目录,index.html 所在位置,所有相对路径基于此目录解析 |
base |
string |
'/' |
否 | 开发和生产的公共基础路径。部署到子路径时必须修改,如 '/my-app/' |
publicDir |
string | false |
'public' |
否 | 静态资源目录,其中的文件直接复制到输出目录。设为 false 禁用此功能 |
plugins |
Plugin[] |
[] |
否 | 插件数组,Vite 和 Rollup 插件均可使用 |
define |
Record<string, any> |
{} |
否 | 定义全局常量替换。值会在编译时直接替换到代码中 |
envDir |
string |
root |
否 | 加载 .env 文件的目录 |
envPrefix |
string | string[] |
'VITE_' |
否 | 以此前缀开头的环境变量才会暴露给客户端代码 |
logLevel |
'info' | 'warn' | 'error' | 'silent' |
'info' |
否 | 控制台输出级别 |
clearScreen |
boolean |
true |
否 | 重新打印日志前是否清屏 |
appType |
'spa' | 'mpa' | 'custom' |
'spa' |
否 | 应用类型。mpa 禁用 SPA 回退,custom 禁用 HTML 处理 |
resolve 参数说明
| 参数 | 类型 | 默认值 | 是否必填 | 说明 |
|---|---|---|---|---|
resolve.alias |
Record<string, string> | Array |
{} |
否 | 路径别名,将 @ 等短路径映射到实际目录,简化 import 路径 |
resolve.extensions |
string[] |
['.mjs', '.js', '.mts', '.ts', '.jsx', '.tsx', '.json'] |
否 | 无扩展名 import 时自动尝试的后缀列表,从左到右依次尝试 |
路径别名配置示例:
import { resolve } from 'path'
export default defineConfig({
resolve: {
alias: {
// 将 @/components/Button 映射到 src/components/Button
'@': resolve(__dirname, 'src'),
// 也可以用数组形式,支持正则
// { find: /^@\//, replacement: resolve(__dirname, 'src') + '/' }
},
},
})
使用别名后,import 可以这样写:
// 不用再写 ../../components/Button
import Button from '@/components/Button.vue'
5. 开发服务器
server 参数说明
| 参数 | 类型 | 默认值 | 是否必填 | 说明 |
|---|---|---|---|---|
server.host |
string | boolean |
'localhost' |
否 | 监听地址。设为 '0.0.0.0' 或 true 可以让局域网内其他设备访问 |
server.port |
number |
5173 |
否 | 开发服务器端口 |
server.strictPort |
boolean |
false |
否 | true 时端口被占用直接报错退出,false 时自动尝试下一个可用端口 |
server.open |
boolean | string |
false |
否 | 启动后自动打开浏览器。传字符串则打开指定路径,如 '/docs' |
server.proxy |
Record<string, ProxyOptions> |
{} |
否 | 反向代理配置,将匹配的请求转发到其他服务器 |
server.cors |
boolean | CorsOptions |
仅允许 localhost | 否 | 配置跨域资源共享。true 允许所有来源 |
server.https |
HttpsServerOptions |
undefined |
否 | 启用 TLS。通常配合 @vitejs/plugin-basic-ssl 使用 |
server.hmr |
boolean | HmrOptions |
true |
否 | 热模块替换配置。false 禁用 HMR |
常用 server 配置示例
export default defineConfig({
server: {
// 监听所有地址,手机也能访问
host: '0.0.0.0',
// 使用 3000 端口
port: 3000,
// 端口被占用时报错而非自动换端口
strictPort: true,
// 启动后自动打开浏览器
open: true,
},
})
代理配置详细示例
开发时前端跑在 localhost:5173,后端 API 跑在 localhost:8080,直接请求会因跨域被浏览器拒绝。用代理可以让 Vite 把 /api 开头的请求转发到后端:
export default defineConfig({
server: {
proxy: {
// 字符串简写:将 /api 开头的请求代理到目标地址
'/api': 'http://localhost:8080',
// 对象写法,支持更多选项
'/api/v2': {
target: 'http://localhost:8080',
// 修改请求头中的 Origin 为目标地址(解决某些服务端的 Host 校验)
changeOrigin: true,
// 重写路径:去掉 /api 前缀
// /api/users → /users
rewrite: (path) => path.replace(/^\/api/, ''),
},
// 代理 WebSocket
'/socket.io': {
target: 'ws://localhost:8080',
ws: true,
},
// 使用正则匹配
'^/fallback/.*': {
target: 'http://jsonplaceholder.typicode.com',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/fallback/, ''),
},
},
},
})
代理选项说明:
| 选项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
target |
string |
- | 转发目标地址 |
changeOrigin |
boolean |
false |
是否修改请求头的 Host 为目标域名 |
rewrite |
Function |
- | 重写请求路径的函数,参数为原始路径字符串 |
ws |
boolean |
false |
是否代理 WebSocket |
secure |
boolean |
true |
目标为 HTTPS 时是否验证证书 |
6. 环境变量
.env 文件加载顺序
Vite 按以下顺序加载环境变量文件,后面的文件优先级更高(会覆盖前面的同名变量):
.env # 所有环境都加载
.env.local # 所有环境都加载,但不提交到 Git(应加入 .gitignore)
.env.[mode] # 只在指定 mode 下加载(如 .env.development、.env.production)
.env.[mode].local # 只在指定 mode 下加载,不提交到 Git
默认 mode:
npm run dev→ mode 为developmentnpm run build→ mode 为production
自定义 mode:
# 加载 .env.staging 文件
vite build --mode staging
VITE_ 前缀规则
只有以 VITE_ 开头的变量才会被暴露给客户端代码(打包到最终产物)。没有此前缀的变量只能在 Vite 配置文件(Node.js 环境)中访问,不会泄露给浏览器。
# .env 文件示例
# 这个变量会暴露给客户端,可在浏览器代码中访问
VITE_API_URL=https://api.example.com
VITE_APP_TITLE=My Application
# 这个变量不会暴露给客户端(没有 VITE_ 前缀)
DATABASE_PASSWORD=secret123
import.meta.env 内置属性
| 属性 | 类型 | 说明 |
|---|---|---|
import.meta.env.MODE |
string |
当前运行的模式,如 'development' 或 'production' |
import.meta.env.BASE_URL |
string |
部署的基础 URL,对应 vite.config.js 中的 base 配置 |
import.meta.env.PROD |
boolean |
是否为生产环境 |
import.meta.env.DEV |
boolean |
是否为开发环境(与 PROD 相反) |
import.meta.env.SSR |
boolean |
是否在服务端渲染环境中运行 |
在代码中使用:
// 获取自定义环境变量
const apiUrl = import.meta.env.VITE_API_URL
// 根据环境执行不同逻辑
if (import.meta.env.DEV) {
console.log('当前为开发环境')
}
if (import.meta.env.PROD) {
// 生产环境才启用统计
initAnalytics()
}
.env 文件示例
# .env.development
VITE_API_URL=http://localhost:8080/api
VITE_APP_TITLE=My App (Dev)
VITE_ENABLE_MOCK=true
# .env.production
VITE_API_URL=https://api.example.com
VITE_APP_TITLE=My App
VITE_ENABLE_MOCK=false
TypeScript 类型补全
默认情况下 import.meta.env 的自定义变量没有类型提示。在 src/vite-env.d.ts 中扩展 ImportMetaEnv 接口来获得类型提示:
// src/vite-env.d.ts
/// <reference types="vite/client" />
interface ImportMetaEnv {
// 在这里声明自定义环境变量的类型
readonly VITE_API_URL: string
readonly VITE_APP_TITLE: string
readonly VITE_ENABLE_MOCK: string // .env 中的值都是字符串
}
interface ImportMeta {
readonly env: ImportMetaEnv
}
7. 静态资源处理
import 资源文件
Vite 支持直接用 import 导入图片、字体等静态资源,返回处理后的资源 URL:
// 导入图片,imgUrl 是处理后的 URL(生产环境会带 hash)
import imgUrl from './assets/logo.png'
// 在代码中使用
const img = document.createElement('img')
img.src = imgUrl
document.body.appendChild(img)
在 Vue 单文件组件中:
<template>
<!-- 模板中可以直接使用相对路径,Vite 自动处理 -->
<img src="./assets/logo.png" alt="logo" />
</template>
<script setup>
import logoUrl from './assets/logo.png'
</script>
查询参数修饰符
导入资源时可以加查询参数改变 Vite 的处理方式:
| 查询参数 | 说明 | 使用场景 |
|---|---|---|
?url |
强制返回 URL 字符串(即使文件很小也不内联) | 需要明确 URL 的情况 |
?raw |
返回文件内容的字符串 | 读取 GLSL shader、文本文件内容 |
?inline |
强制内联为 base64 URL(即使文件较大) | 确保资源内联避免额外请求 |
// ?url:始终获取 URL,不内联
import iconUrl from './icon.svg?url'
// ?raw:获取文件的原始文本内容
import shaderSource from './shader.glsl?raw'
console.log(shaderSource) // 文件的字符串内容
// ?inline:强制 base64 内联
import smallIcon from './icon.png?inline'
// smallIcon 为 'data:image/png;base64,...'
public 目录 vs src/assets 目录的区别
详细比较:
| 特性 | public/ |
src/assets/ |
|---|---|---|
| 处理方式 | 原样复制,不做任何转换 | Vite 处理:压缩、添加 hash 等 |
| 引用方式 | HTML/CSS/JS 中用绝对路径如 /logo.png |
用 import 或相对路径 |
能否用 import |
不能 | 可以 |
| 文件名是否带 hash | 否,文件名不变 | 是,如 logo.a1b2c3.png |
| 浏览器缓存 | 需要服务器配置 Cache-Control | 可长期缓存(hash 变化即更新) |
| 适合什么文件 | robots.txt、favicon、已处理的第三方 JS | 项目自己的图片、字体、图标 |
<!-- public/ 中的文件,用绝对路径引用 -->
<link rel="icon" href="/favicon.ico" />
<img src="/images/hero.jpg" />
// src/assets/ 中的文件,用 import
import heroImage from './assets/images/hero.jpg'
assetsInlineLimit
小于此阈值的资源会被内联为 base64 URL,避免额外的 HTTP 请求。大于此阈值的资源会生成独立文件。
默认值为 4096 字节(4KB)。在 vite.config.js 中配置:
export default defineConfig({
build: {
// 8KB 以下的资源内联,设为 0 禁用内联
assetsInlineLimit: 8192,
},
})
8. 常用插件
官方插件
| 插件 | 安装命令 | 用途 |
|---|---|---|
@vitejs/plugin-vue |
npm i -D @vitejs/plugin-vue |
Vue 3 单文件组件(.vue)支持 |
@vitejs/plugin-vue-jsx |
npm i -D @vitejs/plugin-vue-jsx |
Vue 3 JSX/TSX 支持 |
@vitejs/plugin-react |
npm i -D @vitejs/plugin-react |
React 支持(使用 Babel) |
@vitejs/plugin-react-swc |
npm i -D @vitejs/plugin-react-swc |
React 支持(使用 SWC,速度更快) |
@vitejs/plugin-legacy |
npm i -D @vitejs/plugin-legacy |
兼容旧版浏览器(自动注入 polyfill) |
社区常用插件
| 插件 | 安装命令 | 用途 |
|---|---|---|
vite-plugin-checker |
npm i -D vite-plugin-checker |
在开发服务器中运行 TypeScript/ESLint 检查并显示错误 |
unplugin-auto-import |
npm i -D unplugin-auto-import |
自动导入 Vue/React API,无需手动 import |
unplugin-vue-components |
npm i -D unplugin-vue-components |
Vue 组件自动按需导入 |
@vitejs/plugin-vue 配置示例
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [
vue({
// 包含 Vue 的自定义块(如 <docs>)的文件
include: [/\.vue$/],
// 启用响应式语法糖(实验性)
// reactivityTransform: true,
}),
],
})
@vitejs/plugin-react 配置示例
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [
react({
// 包含 JSX 的文件(默认只处理 .jsx/.tsx)
include: '**/*.{jsx,tsx}',
// Babel 插件,用于添加装饰器等特性
babel: {
plugins: ['@babel/plugin-proposal-decorators'],
},
}),
],
})
@vitejs/plugin-react-swc 配置示例
SWC 是用 Rust 编写的编译器,比 Babel 快很多,推荐新项目使用:
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react-swc'
export default defineConfig({
plugins: [
react({
// 启用 TypeScript 装饰器支持
// tsDecorators: true,
}),
],
})
@vitejs/plugin-legacy 配置示例
用于支持不支持原生 ESM 的旧浏览器(如 IE11 时代的浏览器):
# 需要同时安装 terser
npm install -D @vitejs/plugin-legacy terser
import { defineConfig } from 'vite'
import legacy from '@vitejs/plugin-legacy'
export default defineConfig({
plugins: [
legacy({
// 目标浏览器,使用 browserslist 语法
targets: ['defaults', 'not IE 11'],
// 额外的 polyfill
additionalLegacyPolyfills: ['regenerator-runtime/runtime'],
}),
],
})
vite-plugin-checker 配置示例
在浏览器中显示 TypeScript 编译错误(而不只是在终端):
npm install -D vite-plugin-checker
import { defineConfig } from 'vite'
import checker from 'vite-plugin-checker'
export default defineConfig({
plugins: [
checker({
// 启用 TypeScript 检查
typescript: true,
// 启用 ESLint 检查
eslint: {
lintCommand: 'eslint "./src/**/*.{ts,tsx,vue}"',
},
}),
],
})
unplugin-auto-import 配置示例
自动导入常用 API,无需每个文件都写 import { ref, computed } from 'vue':
npm install -D unplugin-auto-import
import { defineConfig } from 'vite'
import AutoImport from 'unplugin-auto-import/vite'
export default defineConfig({
plugins: [
AutoImport({
// 自动导入的来源
imports: [
'vue', // ref, computed, watch 等
'vue-router', // useRouter, useRoute 等
'pinia', // defineStore, storeToRefs 等
],
// 生成类型声明文件(TypeScript 项目推荐)
dts: 'src/auto-imports.d.ts',
}),
],
})
unplugin-vue-components 配置示例
Vue 组件自动按需导入,无需手动注册组件:
npm install -D unplugin-vue-components
import { defineConfig } from 'vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
export default defineConfig({
plugins: [
Components({
// 组件目录(默认为 src/components)
dirs: ['src/components'],
// 使用 Element Plus 自动按需导入
resolvers: [ElementPlusResolver()],
// 生成类型声明文件
dts: 'src/components.d.ts',
}),
],
})
9. 生产构建基础
构建命令
# 构建生产版本,输出到 dist/ 目录
npm run build
# 等价于
npx vite build
# 预览构建产物(本地启动一个静态文件服务器)
npm run preview
# 等价于
npx vite preview
vite preview 是用来验证构建产物是否正常的工具,它会启动一个简单的 HTTP 服务器提供 dist/ 目录的文件,用于在部署前本地测试。它不是用于生产部署的服务器。
build 参数说明
| 参数 | 类型 | 默认值 | 是否必填 | 说明 |
|---|---|---|---|---|
build.outDir |
string |
'dist' |
否 | 构建输出目录,相对于 root |
build.assetsDir |
string |
'assets' |
否 | 静态资源的存放子目录(在 outDir 内) |
build.target |
string | string[] |
'baseline-widely-available' |
否 | 构建产物的浏览器兼容目标。baseline-widely-available 约等于 Chrome 107+ |
build.minify |
'esbuild' | 'terser' | false |
'esbuild' |
否 | 代码压缩工具。esbuild 速度快,terser 压缩率更高,false 不压缩 |
build.sourcemap |
boolean | 'inline' | 'hidden' |
false |
否 | 是否生成 source map。true 生成独立 .map 文件,'inline' 内联到产物,'hidden' 生成但不在产物中引用 |
build.assetsInlineLimit |
number |
4096 |
否 | 小于此字节数的资源内联为 base64,减少 HTTP 请求 |
完整 build 配置示例
export default defineConfig({
build: {
// 输出到 dist 目录
outDir: 'dist',
// 静态资源放到 dist/assets/ 下
assetsDir: 'assets',
// 目标浏览器:支持现代浏览器
target: 'baseline-widely-available',
// 使用 esbuild 压缩(更快)
minify: 'esbuild',
// 生成 source map(调试生产问题时有用)
sourcemap: false,
// 小于 4KB 的资源内联
assetsInlineLimit: 4096,
// Rollup 打包选项
rollupOptions: {
output: {
// 手动分包:将第三方库单独打包
manualChunks: {
vendor: ['vue', 'vue-router', 'pinia'],
},
},
},
},
})
构建产物结构
dist/
├── index.html
└── assets/
├── index-a1b2c3.js # 主 JS(带内容 hash)
├── vendor-d4e5f6.js # 第三方库(如果有 manualChunks 配置)
├── index-g7h8i9.css # 样式文件
└── logo-j1k2l3.png # 图片资源
10. 常见问题与踩坑
process.env.NODE_ENV 不可用
Webpack 项目中常用 process.env.NODE_ENV 判断环境,但在 Vite 项目中直接使用会报错(浏览器没有 process 对象)。
正确做法是使用 import.meta.env:
// 错误写法(Vite 项目中不可用)
if (process.env.NODE_ENV === 'production') {
// ...
}
// 正确写法
if (import.meta.env.PROD) {
// ...
}
// 或者
if (import.meta.env.MODE === 'production') {
// ...
}
如果引用的第三方库内部使用了 process.env.NODE_ENV,可以在 vite.config.js 中用 define 手动注入:
export default defineConfig({
define: {
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
},
})
部署后资源 404:忘记配置 base
将项目部署到子路径(如 https://example.com/my-app/)时,如果没有配置 base,构建产物中的资源路径仍然是根路径 /,导致 404。
// 错误:部署到子路径时没有配置 base
export default defineConfig({
// 没有 base 配置,默认 '/'
})
// 正确:配置 base 为子路径
export default defineConfig({
base: '/my-app/', // 必须以 / 结尾
})
部署到 GitHub Pages 时通常需要将 base 设为仓库名:
export default defineConfig({
base: '/repo-name/',
})
public 里的文件不能用 import 引用
public/ 目录中的文件不经过 Vite 处理,因此不能用 ES import 语句引用,必须用绝对路径字符串。
// 错误:public 目录的文件不能用 import
import logo from '/public/logo.png' // 报错
// 正确:用绝对路径字符串
const logo = '/logo.png' // public/ 中的文件用根路径
img.src = logo
// 如果需要用 import,应把文件放在 src/assets/ 中
import logoUrl from './assets/logo.png' // 正确
在 HTML 中引用 public 文件:
<!-- 正确:绝对路径 -->
<img src="/logo.png" />
<!-- 错误:不要写成 /public/logo.png -->
<img src="/public/logo.png" />
HMR 失效的常见原因
热更新不生效时,可以从以下方面排查:
-
文件不在 Vite 监听范围内:默认监听
root目录。node_modules中的文件变化不触发 HMR。 -
导出方式问题:组件没有默认导出,或导出方式不标准。
-
副作用阻止了 HMR:模块有不可恢复的副作用(如修改了全局状态),Vite 会回退到全页刷新。
-
网络问题:HMR 通过 WebSocket 通信,如果 WebSocket 连接失败,HMR 就无法工作。可以在
server.hmr中配置:
export default defineConfig({
server: {
hmr: {
// 手动指定 HMR 通信用的 host 和 port
host: 'localhost',
port: 5173,
// 显示 HMR 连接错误
overlay: true,
},
},
})
- 在生产构建后测试 HMR:
vite preview不支持 HMR,HMR 只在开发模式(vite)下有效。
依赖预构建缓存问题
Vite 会把 node_modules 中的依赖预构建后缓存到 node_modules/.vite/deps/ 目录。有时候这个缓存会过期或损坏,导致奇怪的问题(如模块找不到、版本不一致等)。
解决方法:
# 方法一:启动时强制重新预构建(推荐)
npx vite --force
# 方法二:手动删除缓存目录
rm -rf node_modules/.vite
# 方法三:重新安装依赖
rm -rf node_modules
npm install
也可以在配置中控制哪些依赖需要预构建:
export default defineConfig({
optimizeDeps: {
// 强制预构建这些依赖(即使 Vite 自动检测时没发现)
include: ['some-package'],
// 排除某些依赖不进行预构建
exclude: ['another-package'],
},
})
import.meta.env 变量只能用完整变量名
Vite 在构建时通过静态替换来注入环境变量,因此不能用动态方式访问:
// 错误:动态访问,Vite 无法在构建时替换
const key = 'VITE_API_URL'
const value = import.meta.env[key] // 生产构建后可能为 undefined
// 正确:用完整的变量名
const value = import.meta.env.VITE_API_URL
最佳实践
始终通过 import.meta.env.VITE_xxx 访问环境变量,不拼接变量名:Vite 在构建时静态替换 import.meta.env 中的变量,若用变量名拼接(如 `VITE_${key}`),构建时无法识别,运行时返回 undefined。
生产构建前运行 vite preview 验证产物:dev 模式和 build 产物的行为可能不同(如路径别名、资源引用),用 preview 在本地模拟生产环境,比直接部署再排查问题高效。
合理配置 optimizeDeps.include 加速冷启动:对于较大的 CommonJS 依赖(如某些 UI 库),预构建默认有时无法自动检测到,手动加入 include 列表可避免首次访问时再编译的延迟。
使用路径别名(@ → src/)替代相对路径导入:深层嵌套的 ../../../components/xxx 难以维护,配置 resolve.alias 后统一用 @/components/xxx,移动文件时只需修改别名配置一处。
// vite.config.ts
import { resolve } from 'path'
export default defineConfig({
resolve: {
alias: { '@': resolve(__dirname, 'src') }
}
})
常见陷阱
陷阱:修改 vite.config.ts 后开发服务器未重启,配置未生效
现象: 修改了 vite.config.ts 的 server.port 或 resolve.alias,但行为没有变化。
原因: Vite 开发服务器在启动时读取配置,配置变更后需要重启服务器才生效;部分配置(如插件列表)不支持热更新。
解决: 停止 vite 进程后重新启动;或检查 Vite 版本是否支持配置热重载(--config flag 变更会自动重启)。
陷阱:import 图片路径在构建后变为 undefined
现象: 开发环境图片正常显示,vite build 后图片加载 404 或 src 为 undefined。
原因: 用字符串拼接动态路径(如 `/assets/${name}.png`)无法被 Vite 静态分析,不会被打包处理;Vite 只处理静态 import 或 new URL(path, import.meta.url) 形式。
解决: 用 new URL(./assets/${name}.png, import.meta.url).href 动态引用资源;或将动态图片放到 public/ 目录(不经过 Vite 处理,路径保持不变)。
陷阱:process.env.NODE_ENV 在 Vite 项目中无法访问
现象: 代码中使用 process.env.NODE_ENV,浏览器报 process is not defined。
原因: Vite 不自动注入 process 全局变量(这是 Webpack 的行为);Vite 使用 import.meta.env.MODE 代替。
解决: 将 process.env.NODE_ENV 替换为 import.meta.env.MODE;或在 vite.config.ts 中配置 define: { 'process.env.NODE_ENV': JSON.stringify(mode) } 手动注入。