POST · 前端 · 文章
Vite 从入门到进阶
2026/11/13 · by ZHANG XING YUN · 阅读 1 次 · 3215 字 · 预计阅读 9 分钟
本文整理 Vite 学习路径:从核心原理与最小项目起步,逐层深入配置体系、环境变量、插件开发、按需加载、生产构建优化与部署策略,最后给出与 Webpack 的对比选型、常见问题。
- 适用场景:Web / SPA / 微前端 / Electron 渲染层的现代构建
- 技术栈:Vite 7.x,框架无关(Vue / React / Svelte 等均可接入)
- 目标:开发极速、配置简洁、生产可控、迁移平滑
一、先明确它在解决什么问题
Vite 的核心定位是下一代前端构建工具,它要解决的核心痛点是:传统打包器(Webpack)在大型项目中开发服务器启动慢、热更新慢。
| 问题 | Vite 的答案 |
|---|---|
| 冷启动要等几十秒? | 开发环境不打包,按需编译,启动近乎瞬时 |
| 改一行代码 HMR 要等几秒? | 只编译变更模块,HMR 通常 < 100ms |
| 配置太复杂? | 开箱即用,内置 TS/JSX/CSS/静态资源支持 |
| 生产构建慢? | 基于 Rolldown(或 Rollup),构建速度与产物体积表现优秀 |
需要理解的边界:
| 维度 | Vite 的表现 |
|---|---|
| 开发体验 | 极快,项目规模增大时优势更明显 |
| 生产构建 | 成熟稳定,但与开发环境的“快”是两套机制 |
| 生态成熟度 | 快速增长,主流框架与工具均已支持 |
| 旧浏览器 | 默认面向现代浏览器,需 @vitejs/plugin-legacy 兼容 |
三个原则:
- 开发靠浏览器,生产靠打包器:理解这两套机制,才能正确配置与优化。
- 配置即约定:默认配置覆盖大多数场景,不要过度定制。
- 生产优化靠 Rollup 配置:
build.rollupOptions是进阶优化的关键入口。
二、入门:环境与最小项目
2.1 环境要求
Vite 需要 Node.js 20.19+ 或 22.12+ 。验证:
node -v
2.2 创建项目
官方脚手架 create-vite 覆盖主流框架:
npm create vite@latest my-app -- --template vue-ts
可用模板:vanilla、vue、react、svelte、solid、lit、qwik 及其 -ts 变体 。
进入目录安装并启动:
cd my-app
npm install
npm run dev
浏览器访问 http://localhost:5173 即可。
2.3 最小项目结构
my-app/
├─ index.html # 入口 HTML,Vite 的构建入口
├─ src/
│ └─ main.ts # 应用入口
├─ vite.config.ts # 配置文件(可选)
└─ package.json
与 Webpack 的关键区别:HTML 是入口,不是 JS。Vite 直接以 index.html 为起点,<script type="module" src="/src/main.ts"> 引用的模块会被自动处理 。
2.4 常用脚本
{
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
}
}
preview 用于本地预览生产构建产物,不要用 dev 验证生产行为。
三、核心原理:为什么快
3.1 开发环境:按需编译
Webpack 的做法是:启动时先打包整个依赖图,再启动服务器。项目越大,启动越慢。
Vite 的做法是:直接启动服务器,浏览器请求哪个模块才编译哪个 。
浏览器请求 /src/main.ts
↓
Vite Dev Server 实时编译(esbuild/oxc)
↓
返回 ESM 模块给浏览器
↓
浏览器原生 ESM 执行
效果:冷启动时间与项目规模解耦。实测中型 Vue 项目(120+ 文件),Webpack 冷启动 8.2s,Vite 0.8s 。
3.2 热更新:只动变更的那一块
Webpack 的 HMR 需要重新计算依赖链并打包。Vite 的 HMR 只让变更模块及其 HMR 边界失效,重新编译单个文件即可 。
实测 JS 文件 HMR:Webpack 800ms~1.2s,Vite < 100ms 。
3.3 生产环境:打包是另一回事
开发环境的“不打包”只在 dev server 生效。执行 vite build 时,Vite 使用 Rolldown(新版本)或 Rollup 进行完整打包,并做 Tree Shaking、代码分割、压缩等优化 。
四、配置体系
4.1 配置文件
vite.config.ts 是默认配置入口,使用 defineConfig 获得类型提示:
import { defineConfig } from "vite";
export default defineConfig({
server: {
host: "127.0.0.1",
port: 5173,
strictPort: true
},
build: {
outDir: "dist",
sourcemap: true
}
});
4.2 条件配置
根据 command(serve / build)或 mode 返回不同配置 :
export default defineConfig(({ command, mode }) => {
if (command === "serve") {
return { server: { port: 5173 } };
}
return { build: { sourcemap: false } };
});
4.3 常用配置项
| 配置 | 作用 | 建议 |
|---|---|---|
server.port |
开发端口 | 固定端口便于调试 |
resolve.alias |
路径别名 | 与 tsconfig paths 保持一致 |
server.proxy |
开发代理 | 解决 CORS |
build.target |
构建目标 | 现代项目用默认或 esnext |
build.rollupOptions |
Rollup 配置 | 分包、外部依赖 |
五、环境变量与模式
5.1 基本规则
只有以 VITE_ 为前缀的变量才会暴露给客户端代码,通过 import.meta.env 访问 :
# .env.development
VITE_API_URL=http://localhost:8080/api
const apiUrl = import.meta.env.VITE_API_URL;
5.2 内置变量
| 变量 | 含义 |
|---|---|
import.meta.env.MODE |
当前模式(development / production) |
import.meta.env.DEV |
是否开发环境 |
import.meta.env.PROD |
是否生产环境 |
import.meta.env.BASE_URL |
部署基础路径 |
5.3 配置文件中读取环境变量
在 vite.config.ts 运行时,.env 文件尚未加载到 process.env。如果配置本身需要环境变量(如端口、代理目标),用 loadEnv :
import { defineConfig, loadEnv } from "vite";
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd(), "");
return {
server: { port: Number(env.APP_PORT || 5173) }
};
});
六、静态资源与 CSS
6.1 资源导入
import logoUrl from "./assets/logo.png"; // 返回 URL
import content from "./file.txt?raw"; // 返回文本内容
import Worker from "./worker?worker"; // Web Worker
批量导入用 import.meta.glob:
const modules = import.meta.glob("./assets/*.png", { eager: true });
6.2 CSS 处理
Vite 内置支持 CSS、CSS Modules、PostCSS 和预处理器(需安装对应依赖):
import "./style.css"; // 全局样式
import styles from "./Component.module.css"; // CSS Modules
预处理器安装:
npm install -D sass less stylus
无需额外 loader 配置,Vite 自动识别。
6.3 资源内联阈值
build: {
assetsInlineLimit: 4096 // 小于 4KB 的资源转为 base64
}
七、插件开发
7.1 插件结构
Vite 插件基于 Rollup 插件接口,并扩展了 Vite 特有钩子 :
import type { Plugin } from "vite";
export function myPlugin(): Plugin {
return {
name: "my-plugin",
apply: "build",
transform(code, id) {
if (id.endsWith(".js")) {
return { code: `/* built with vite */\n${code}`, map: null };
}
}
};
}
7.2 钩子执行顺序
config → configResolved → configureServer(dev) → buildStart
→ resolveId → load → transform → buildEnd → closeBundle
7.3 Vite 特有钩子
| 钩子 | 用途 |
|---|---|
config |
修改配置 |
configResolved |
读取最终配置 |
configureServer |
开发服务器中间件 |
transformIndexHtml |
修改 HTML |
handleHotUpdate |
自定义 HMR 行为 |
7.4 常用社区插件
| 插件 | 作用 |
|---|---|
@vitejs/plugin-vue / @vitejs/plugin-react |
框架支持 |
unplugin-auto-import |
自动导入 API |
unplugin-vue-components |
组件自动注册 |
vite-plugin-compression |
生成 gzip/brotli |
八、按需加载与代码分割
8.1 动态导入
const module = await import("./heavy-module");
Vite 自动将其拆分为独立 chunk。
8.2 路由懒加载
const routes = [
{ path: "/", component: () => import("@/pages/Home.vue") },
{ path: "/about", component: () => import("@/pages/About.vue") }
];
8.3 手动分包
在 build.rollupOptions.output.manualChunks 中配置:
build: {
rollupOptions: {
output: {
manualChunks: {
vendor: ["vue", "vue-router", "pinia"],
charts: ["echarts"]
}
}
}
}
九、生产构建优化
9.1 构建目标
Vite 默认目标是 baseline-widely-available(约 2023 年中发布的浏览器版本)。如果确定用户环境现代,可设为 esnext 获得更小体积 :
build: { target: "esnext" }
需要兼容旧浏览器时,使用 @vitejs/plugin-legacy,它会为旧浏览器生成降级 chunk 。
9.2 压缩
Vite 默认用 esbuild(或 oxc)压缩,速度极快。如果追求极致体积,可切换 Terser(体积约小 2~5%,但速度慢):
build: {
minify: "terser",
terserOptions: { compress: { drop_console: true } }
}
9.3 移除调试代码
生产环境移除 console 和 debugger:
build: {
terserOptions: {
compress: { drop_console: true, drop_debugger: true }
}
}
9.4 构建报告
npx vite build --report
生成 dist/stats.html,可视化 chunk 构成与体积大头 。
9.5 分包策略
| 策略 | 做法 |
|---|---|
| 路由级拆分 | 动态 import() |
| 第三方库拆分 | manualChunks 将 vue/react/echarts 等独立 |
| 运行时拆分 | Vite 默认处理,无需额外配置 |
十、部署与缓存策略
10.1 基础路径
如果部署在子路径下:
export default defineConfig({ base: "/my-app/" });
10.2 缓存头
Vite 构建产物默认带 content hash,可设置长期缓存 :
| 文件 | Cache-Control |
|---|---|
assets/*.[hash].js/css |
public, max-age=31536000, immutable |
index.html |
no-cache |
10.3 压缩传输
在 Nginx 或 CDN 层开启 gzip/brotli,或使用 vite-plugin-compression 预生成压缩文件。
10.4 部署平台
| 平台 | 要点 |
|---|---|
| Nginx | try_files $uri $uri/ /index.html(SPA 路由) |
| Vercel / Netlify | 自动识别 Vite,无需额外配置 |
| 对象存储 + CDN | 注意缓存头与压缩配置 |
十一、调试与产物分析
| 问题 | 手段 |
|---|---|
| 产物过大 | vite build --report + stats.html |
| 构建慢 | --debug 查看插件耗时,检查是否有重插件 |
| 依赖重复 | npm ls <包名> / pnpm why <包名> 检查多版本共存 |
| HMR 不生效 | 检查模块是否在 HMR 边界内,或是否有插件拦截 |
| 生产白屏 | 检查 base 配置、路由模式、资源路径 |
十二、与 Webpack 的对比
| 维度 | Vite | Webpack 5 |
|---|---|---|
| 开发启动 | 按需编译,极快,与规模解耦 | 全量打包,随规模变慢 |
| HMR | < 100ms | 800ms~1.2s |
| 配置复杂度 | 低,开箱即用 | 高,需理解 entry/output/loader/plugin |
| 生态 | 快速增长,现代插件为主 | 极其成熟,历史积累深厚 |
| 生产构建 | Rolldown/Rollup,质量高 | 成熟,优化手段丰富 |
| 旧浏览器 | 需 plugin-legacy | 配置 target 即可 |
| 适用场景 | 新项目、追求开发体验 | 存量大型项目、需要极致控制 |
选型建议 :
- 新项目:优先 Vite,开发体验优势明显。
- 存量 Webpack 项目:如果构建稳定且无痛点,不急于迁移;如果需要提速,可评估 Rspack(兼容 Webpack 配置的 Rust 实现)。
- 需要大量 Webpack 专属插件:继续用 Webpack 或 Rspack。
十三、迁移要点
从 Webpack 迁移到 Vite 的核心步骤:
| 步骤 | 工作 |
|---|---|
| 1. 入口调整 | 确保有 index.html,<script type="module"> 指向入口 |
| 2. 环境变量 | process.env.VUE_APP_* → import.meta.env.VITE_* |
| 3. 静态资源 | require() 改为 import 或 new URL() |
| 4. 别名 | resolve.alias 迁移到 Vite 的 resolve.alias |
| 5. 代理 | devServer.proxy → server.proxy |
| 6. 插件替换 | Webpack 专属插件找 Vite 替代品或移除 |
| 7. 构建验证 | 对比产物大小、首屏加载、路由懒加载是否正常 |
十四、常见问题
| 现象 | 原因 | 处理 |
|---|---|---|
| 生产构建后首屏很慢 | 未做路由懒加载,全部代码打进入口 | 用动态 import() 拆分路由 |
| 某依赖在构建后重复出现 | 多版本共存 | npm ls 排查,统一版本或去重 |
| 静态资源 404 | base 配置与部署路径不一致 |
设置正确的 base |
| 环境变量读不到 | 缺少 VITE_ 前缀 |
改名为 VITE_XXX |
| HMR 不工作 | 插件拦截或模块不在边界内 | 检查插件 handleHotUpdate |
| 旧浏览器白屏 | 默认 target 不兼容 | 使用 @vitejs/plugin-legacy |
vite.config 读不到 .env |
配置运行时 .env 未加载 |
用 loadEnv |
| 构建产物过大 | 未压缩、未分包、target 过旧 | 检查 minify、manualChunks、target |
十五、FAQ
Q1:Vite 和 Webpack 怎么选?
新项目、追求开发体验选 Vite;存量 Webpack 项目稳定运行则不必迁移,除非开发体验成为瓶颈。两者生产构建质量相当,Vite 开发优势压倒性 。
Q2:Vite 开发快,生产构建也快吗?
生产构建基于 Rolldown/Rollup,速度优于 Webpack,但不如开发服务器的“瞬时”体验。实测中型项目 Vite 构建约 6s,Webpack 约 18s 。
Q3:为什么开发环境不需要打包?
现代浏览器原生支持 ES Module。Vite 利用这一点,让浏览器直接请求模块,服务器按需编译返回,省去了打包步骤 。
Q4:生产环境可以不打包吗?
不推荐。生产环境需要 Tree Shaking、代码分割、压缩、hash 命名等优化,这些依赖打包器完成。Vite 的 build 就是做这件事。
Q5:import.meta.env 和 process.env 有什么区别?
process.env 在 Vite 客户端代码中不可用(除非手动 define)。import.meta.env 是 Vite 注入的,只有 VITE_ 前缀的变量会暴露 。
Q6:如何兼容旧浏览器?
使用 @vitejs/plugin-legacy,它生成两套 chunk:现代浏览器加载 ESM,旧浏览器加载降级版本 。
Q7:Vite 插件和 Rollup 插件什么关系?
Vite 插件兼容 Rollup 插件接口,同时扩展了 Vite 特有钩子(configureServer、transformIndexHtml 等)。
Q8:如何减小产物体积?
按优先级:路由懒加载 → 第三方库按需引入 → 设置 target: 'esnext'(如环境允许)→ 开启 drop_console → 检查是否有重复依赖 。
Q9:vite preview 能替代生产服务器吗?
不能。preview 仅用于本地验证构建产物,不具备生产级的缓存、压缩、并发能力。生产部署用 Nginx / CDN。
Q10:Vite 支持 SSR 吗?
支持。Vite 提供 SSR 相关 API,Nuxt、SvelteKit、Astro 等框架均基于 Vite 构建 SSR/SSG 应用。
附录:配置模板与检查清单
vite.config.ts 模板
import { defineConfig, loadEnv } from "vite";
import vue from "@vitejs/plugin-vue";
import { compression } from "vite-plugin-compression2";
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd(), "");
return {
base: "/",
plugins: [
vue(),
compression({ algorithm: "gzip" }),
compression({ algorithm: "brotliCompress", ext: ".br" })
],
resolve: {
alias: { "@": "/src" }
},
server: {
port: 5173,
proxy: {
"/api": { target: env.VITE_API_TARGET || "http://localhost:8080", changeOrigin: true }
}
},
build: {
target: "esnext",
sourcemap: false,
chunkSizeWarningLimit: 800,
rollupOptions: {
output: {
manualChunks: {
vendor: ["vue", "vue-router", "pinia"]
}
}
}
}
};
});