Vite 是现代前端项目常用的开发服务器和构建工具。
对于 React + TypeScript + Vite 项目,它主要负责:
- 开发阶段:启动开发服务器,提供快速模块加载和热模块替换(HMR)。
- 生产构建阶段:将 TypeScript、JSX、CSS 等源码转换、优化并打包为浏览器可以加载的静态资源。
整体流程:
React / TypeScript Source
│
▼
Vite
│
┌─────┴─────┐
▼ ▼
Development Production
│ │
Dev Server Build
│ │
▼ ▼
HMR / ESM dist/
│
▼
Nginx / CDN
│
▼
Browser
对于普通 React SPA,Vite 不需要在生产服务器上持续运行。构建后的 HTML、JavaScript、CSS 等资源可以直接由 Nginx 提供。
1. 开发服务器(Dev Server)
启动命令
pnpm dev
通常对应 package.json:
{
"scripts": {
"dev": "vite"
}
}
默认开发地址通常是:
http://localhost:5173
常见命令:
# 启动开发服务器
pnpm dev
# 指定端口
pnpm dev -- --port 3000
# 允许局域网或容器外访问
pnpm dev -- --host 0.0.0.0
# 强制重新预构建依赖
pnpm dev -- --force
pnpm dev -- 中的 -- 表示将后续参数传递给脚本执行的程序。
开发服务器的工作方式
传统 Bundler 通常需要先构建一部分或全部模块,才能提供开发服务。
Vite 则利用浏览器原生 ES Modules(ESM),按需转换和提供源码模块。
Browser
│
├── GET /src/main.tsx
│
├── GET /src/App.tsx
│
└── GET /src/components/Header.tsx
│
▼
Vite Dev Server
│
▼
Transform / Serve
这减少了大型项目启动时必须进行的工作。
不过 Vite 在开发阶段仍然会执行模块转换及依赖预构建,并不是完全不进行任何构建处理。
2. HMR(Hot Module Replacement)
HMR 是 Vite 开发体验中的核心机制之一。
当源码发生变化时:
Modify React Component
↓
Vite Detects Changes
↓
HMR Update
↓
Browser Replaces Module
↓
Page Updates
HMR 可以只更新发生变化的模块,而不是重新加载整个页面。
React Fast Refresh
React 项目通常使用:
import react from "@vitejs/plugin-react";
React Fast Refresh 基于 HMR,可以在满足条件时更新组件,并尽可能保留 React State。
例如修改:
export function Counter() {
const [count, setCount] = useState(0);
return <button>{count}</button>;
}
组件代码修改后,计数器的状态在某些情况下可以被保留。
注意:
- HMR 是通用模块热替换机制。
- Fast Refresh 是专门针对 React 组件的热更新机制。
- Fast Refresh 不能保证每一次修改都保留组件状态。
3. 依赖预构建(Dependency Pre-Bundling)
Vite 开发阶段通常会预处理部分第三方依赖。
例如:
import React from "react";
import axios from "axios";
这些依赖可能涉及:
- CommonJS 转 ESM。
- 大量内部模块。
- 多层依赖引用。
Vite 会使用依赖优化机制,将适合预构建的依赖提前转换和合并,以提升开发阶段的加载效率。
缓存通常位于:
node_modules/.vite/
optimizeDeps
可以配置:
export default defineConfig({
optimizeDeps: {
include: ["react", "react-dom"],
exclude: [],
},
});
| 配置 | 作用 |
|---|---|
include | 显式指定需要预构建的依赖 |
exclude | 排除某些依赖的预构建 |
force | 强制重新优化依赖 |
通常不需要手动修改 optimizeDeps。
只有遇到特定依赖加载缓慢、CommonJS 兼容性问题或依赖发现异常时,才值得调整。
依赖预构建主要是开发环境的优化机制,不等于生产环境的 Code Splitting。
4. 生产构建(Production Build)
运行:
pnpm build
通常对应:
{
"scripts": {
"build": "tsc -b && vite build"
}
}
执行流程:
TypeScript Type Check
↓
Vite Build
↓
Module Bundling
↓
Tree Shaking
↓
Minification
↓
dist/
注意:vite build 本身主要负责构建,不会像 tsc --noEmit 一样执行完整的 TypeScript 语义类型检查。
因此 React + TypeScript 项目通常会单独运行:
tsc -b
构建产物
通常生成:
dist/
├── index.html
└── assets/
├── index-A1b2C3.js
├── index-D4e5F6.css
├── MapPage-K7m8N9.js
└── logo-P1q2R3.svg
这些文件就是部署到 Nginx 或 CDN 的静态资源。
build 配置
export default defineConfig({
build: {
outDir: "dist",
sourcemap: false,
emptyOutDir: true,
chunkSizeWarningLimit: 500,
},
});
| 配置 | 作用 |
|---|---|
outDir | 构建结果输出目录 |
sourcemap | 是否生成 Source Map |
emptyOutDir | 构建前是否清理输出目录 |
chunkSizeWarningLimit | Chunk 大小警告阈值 |
target | 构建目标浏览器环境 |
minify | 是否及如何压缩代码 |
chunkSizeWarningLimit 只控制警告阈值,并不会实际降低 Bundle 大小。
5. Preview:预览生产构建
pnpm build
pnpm preview
通常对应:
{
"scripts": {
"preview": "vite preview"
}
}
作用:
dist/
↓
Vite Preview Server
↓
Browser
可以用来检查:
- 构建后的页面是否正常。
- 动态导入是否正确。
- 静态资源路径是否有效。
- 页面路由是否可以访问。
vite preview 主要用于本地预览,不建议将它作为正式生产服务器。
6. vite.config.ts
Vite 的主要配置文件通常是:
vite.config.ts
React + TypeScript + Tailwind CSS 项目示例:
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import tailwindcss from "@tailwindcss/vite";
export default defineConfig({
plugins: [react(), tailwindcss()],
server: {
host: "127.0.0.1",
port: 5173,
proxy: {
"/api": {
target: "http://localhost:8000",
changeOrigin: true,
},
},
},
build: {
outDir: "dist",
sourcemap: false,
},
resolve: {
tsconfigPaths: true,
},
base: "/",
});
这里的 resolve.tsconfigPaths 适用于支持该选项的较新版本 Vite;旧版本可以继续使用 resolve.alias。
常用配置分类
| 配置 | 作用 |
|---|---|
plugins | 加载插件 |
server | 开发服务器配置 |
server.proxy | 开发环境反向代理 |
build | 生产构建 |
resolve | 模块路径解析 |
base | 资源公共路径 |
define | 定义编译时常量 |
css | CSS 处理 |
preview | 预览服务器配置 |
optimizeDeps | 开发依赖优化 |
7. 路径别名(Alias)
假设代码结构:
src/
├── components/
│ └── Button.tsx
├── pages/
└── main.tsx
如果不使用 Alias:
import { Button } from "../../components/Button";
使用 Alias:
import { Button } from "@/components/Button";
TypeScript 配置
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
}
}
Vite 配置
方式一:使用 resolve.alias。
import { fileURLToPath, URL } from "node:url";
export default defineConfig({
resolve: {
alias: {
"@": fileURLToPath(new URL("./src", import.meta.url)),
},
},
});
方式二:较新版本 Vite 可使用:
resolve: {
tsconfigPaths: true,
}
使 Vite 根据匹配的 tsconfig.json 解析路径别名。
注意:这项功能可能带来一定解析开销,且不能解决所有 CSS 或第三方工具中的路径解析问题。
8. 开发代理(server.proxy)
开发中,React 和 Django 可能运行在不同端口:
React → localhost:5173
Django → localhost:8000
可以配置:
server: {
proxy: {
"/api": {
target: "http://localhost:8000",
changeOrigin: true,
},
},
},
浏览器请求:
GET http://localhost:5173/api/v1/pois/
Vite 将请求转发至:
GET http://localhost:8000/api/v1/pois/
这样前端代码可以使用相对地址:
axios.get("/api/v1/pois/");
不需要在开发环境中直接请求另一个 Origin。
生产环境
server.proxy 只用于 Vite 开发服务器。
生产时通常由 Nginx 负责:
Browser
↓
Nginx
├── /assets/ → Static Files
├── /api/ → Django
└── / → React SPA
9. 环境变量(Environment Variables)
Vite 使用 .env 文件管理不同环境的配置。
常见文件:
.env
.env.local
.env.development
.env.production
.env.staging
示例
.env.development:
VITE_API_URL=http://localhost:8000
.env.production:
VITE_API_URL=https://api.example.com
代码中读取:
const apiUrl = import.meta.env.VITE_API_URL;
内置环境变量
| 变量 | 作用 |
|---|---|
import.meta.env.MODE | 当前 Vite Mode |
import.meta.env.DEV | 是否为开发模式 |
import.meta.env.PROD | 是否为生产模式 |
import.meta.env.BASE_URL | 资源基础路径 |
import.meta.env.SSR | 是否处于 SSR 构建环境 |
例如:
if (import.meta.env.DEV) {
console.log("Development");
}
生产构建时可以通过静态替换和 Tree Shaking 删除部分开发专用代码。
环境变量安全
默认情况下,只有 VITE_ 前缀的自定义环境变量会暴露到客户端代码。
但需要特别注意:
所有暴露给浏览器的环境变量都应当视为公开信息。
不能放入:
- 数据库密码。
- Django SECRET_KEY。
- 私有 API Token。
- 服务器访问凭据。
Build-time 与 Runtime
Vite 的客户端环境变量通常是在构建时确定的。
.env.production
↓
vite build
↓
JavaScript Bundle
↓
Nginx
如果构建后修改服务器上的 .env.production,已经生成的 JS 文件不会自动改变。
如果需要运行时动态配置,需要额外的配置文件或配置接口机制。
10. 静态资源处理
Vite 主要有两种静态资源管理方式。
src/assets
src/assets/logo.png
使用:
import logo from "@/assets/logo.png";
function Logo() {
return <img src={logo} alt="Logo" />;
}
Vite 构建时会处理资源路径,并可能根据资源内容、大小及配置进行内联或生成带哈希文件。
例如:
/assets/logo-Cx7Ab91.png
public
public/favicon.ico
通常会原样复制到:
dist/favicon.ico
使用:
<img src="/favicon.ico" alt="Favicon" />
两者区别
| 特性 | src/assets | public |
|---|---|---|
| 经过构建资源处理 | 是 | 通常否 |
| 自动 Hash | 通常支持 | 不自动添加 |
| 可以通过 import 引用 | 是 | 通常不推荐 |
| 固定 URL | 不一定 | 是 |
| 适用场景 | 应用资源 | favicon、固定路径文件 |
普通应用图片、字体等资源通常优先放在 src/assets。
需要保持固定文件名或固定 URL 的资源可以放在 public。
11. Code Splitting(代码拆分)
Code Splitting 用于将 JavaScript 拆分成多个 Chunk,避免用户首次访问时必须下载全部应用代码。
Dynamic Import
import { lazy, Suspense } from "react";
const MapPage = lazy(() => import("./pages/MapPage"));
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<MapPage />
</Suspense>
);
}
Vite 在生产构建时可以将动态导入的模块拆成独立 Chunk。
例如:
dist/assets/
├── index-A1b2.js
├── MapPage-C3d4.js
└── maplibre-E5f6.js
实际拆分结果取决于模块依赖关系与构建配置。
Lazy Loading 与 Code Splitting
两者相关,但并不完全相同。
- Code Splitting:将代码分成多个可独立加载的文件。
- Lazy Loading:推迟加载代码或资源,直到需要使用时。
典型流程:
User Opens Homepage
↓
main.js
↓
User Opens Map Page
↓
MapPage Chunk
↓
MapLibre Chunk
如果 MapPage 在首页就被立即渲染,那么即使使用了 lazy(),相关模块也可能在首页加载。
真正的页面级懒加载需要与路由或条件渲染配合。
12. Tree Shaking
Tree Shaking 用于移除构建过程中确定未被使用的代码。
例如:
export function a() {}
export function b() {}
export function c() {}
只引用:
import { a } from "./utils";
在满足静态分析和副作用处理条件时,Vite 可以在生产构建中移除没有使用的 b() 和 c()。
局限性
Tree Shaking 可能受到以下因素影响:
- CommonJS。
- 动态引用。
- 模块副作用。
- 第三方依赖打包方式。
- 构建器无法证明代码未使用。
因此,使用 ES Modules 有助于构建工具进行静态分析,但不保证所有未使用代码都能被删除。
13. Minification(代码压缩)
生产构建通常会对 JavaScript 和 CSS 进行压缩。
例如源码:
function calculateTotal(price: number, quantity: number) {
return price * quantity;
}
压缩后可能变成:
function t(n, r) {
return n * r;
}
Minification 主要包括:
- 删除不必要的空白和注释。
- 缩短局部标识符。
- 简化表达式。
- 删除可证明无用的代码。
Minification 与 gzip/Brotli
它们解决的问题不同:
| 技术 | 作用阶段 | 核心作用 |
|---|---|---|
| Minification | Vite 构建 | 减少代码本身的体积 |
| gzip | HTTP 传输 | 压缩网络传输数据 |
| Brotli | HTTP 传输 | 通常提供更高压缩率 |
| Lazy Loading | 浏览器加载 | 推迟不需要的代码下载 |
典型过程:
Source Code
↓
Vite Minification
↓
Minified JavaScript
↓
Nginx Brotli / gzip
↓
Network Transfer
↓
Browser Decompression
↓
Parse / Compile / Execute
gzip/Brotli 不会减少浏览器解压后需要处理的 JavaScript 代码量。
14. Bundle 分析与优化
当依赖体积较大,例如 MapLibre 时,可以分析构建产物。
常见优化方式
- 使用页面级 Lazy Loading。
- 避免在入口文件直接引入大型依赖。
- 采用 Dynamic Import。
- 利用 Tree Shaking。
- 合理拆分 Chunk。
- 启用 HTTP 压缩与缓存。
manualChunks
某些 Vite 版本和底层构建器支持手动分包配置,例如:
build: {
rollupOptions: {
output: {
manualChunks: {
map: ["maplibre-gl"],
},
},
},
},
注意:这是基于传统 Rollup 配置的示例。较新版本 Vite 的底层构建器和对应配置 API 可能不同,使用时应确认版本兼容性。
而且手动 Chunk 拆分不一定能缩小总代码体积,也不保证相关 Chunk 不会在首屏加载。
一般建议先使用动态导入和默认代码拆分策略,只有经过 Bundle 分析后才手动调整 Chunk。
构建体积警告
Some chunks are larger than 500 kB
这是构建工具提醒某些 Chunk 较大,不代表构建失败,也不意味着页面一定存在严重性能问题。
需要同时考虑:
- 网络压缩后的大小。
- 是否属于首屏资源。
- 是否长期缓存。
- JavaScript 解析和执行开销。
- 实际设备性能。
15. Source Map
生产代码通常经过 Minification,因此错误堆栈可能不容易阅读。
例如:
function a(e) {
return e + 1;
}
Source Map 可以帮助将构建后的代码位置映射回原始源码。
Production Error
↓
Minified JS Position
↓
Source Map
↓
src/components/Map.tsx:32
配置:
build: {
sourcemap: true,
},
常见选项
| 配置 | 作用 |
|---|---|
false | 不生成 |
true | 生成独立 Source Map |
"inline" | 内嵌到构建文件 |
"hidden" | 生成独立 Source Map,但不添加 Source Mapping URL 注释 |
对于 Sentry 等错误监控服务,可以考虑使用 hidden 并上传 Source Map。
需要注意:Source Map 可能暴露源码结构,因此不一定适合直接公开部署。
16. Base:部署基础路径
base 用于配置应用公开访问时的资源基础路径。
部署在根路径
https://example.com/
配置:
base: "/",
部署在子路径
https://example.com/my-app/
配置:
base: "/my-app/",
Vite 构建时会根据 base 调整:
- JS 资源引用。
- CSS 资源引用。
- 静态资源路径。
- HTML 中相关资源地址。
注意:base 不会自动配置 React Router。
如果 React Router 使用 Browser History,并部署在子路径下,还需要正确配置 Router 的 basename。
17. Plugin:Vite 插件系统
Vite 通过插件扩展功能。
常见插件:
| Plugin | 作用 |
|---|---|
@vitejs/plugin-react | React 支持和 Fast Refresh |
@tailwindcss/vite | Tailwind CSS 集成 |
vite-plugin-svgr | 将 SVG 作为 React 组件 |
vite-plugin-pwa | PWA 支持 |
| Bundle Analyzer 类插件 | 分析构建产物 |
| TypeScript/ESLint 检查插件 | 辅助开发检查 |
例如:
export default defineConfig({
plugins: [react(), tailwindcss()],
});
Vite 的插件机制与 Rollup 插件体系有兼容关系,但 Vite 还提供面向开发服务器和模块转换的专有能力。
18. Vite 与 Nginx 生产部署
Vite 负责构建静态资源,Nginx 负责生产环境的 HTTP 服务。
React Source
↓
Vite Build
↓
dist/
↓
Docker Image
↓
Nginx
↓
Browser
Docker 多阶段构建
# Stage 1: Build
FROM node:24-alpine AS build
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN corepack enable && pnpm install --frozen-lockfile
COPY . .
RUN pnpm build
# Stage 2: Serve
FROM nginx:alpine
COPY --from=build /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
这样最终运行的镜像不需要包含 Node.js 开发依赖。
Nginx SPA 配置
server {
listen 80;
root /usr/share/nginx/html;
index index.html;
location /assets/ {
expires 1y;
add_header Cache-Control "public, immutable";
}
location / {
try_files $uri $uri/ /index.html;
}
location = /index.html {
add_header Cache-Control "no-cache";
}
}
这里假设 Vite 默认使用 /assets/ 输出带哈希的静态资源。
为什么 index.html 不能长期缓存?
Vite 构建文件通常包含内容哈希:
index-A1b2C3.js
代码更新后:
index-X9y8Z7.js
因此带哈希的文件可以长期缓存。
但 index.html 必须能够及时发现最新构建版本,因为它负责引用新的 JS/CSS 文件。
推荐:
index.html
↓
no-cache(使用前重新验证)
assets/*.js
assets/*.css
↓
Long-term Cache
注意:对于 SPA,还应确保不存在的静态资源不会错误地回退成 index.html,否则可能产生 JavaScript MIME Type 错误。
19. Vite、Nginx 与浏览器的职责
| 技术 | 主要职责 |
|---|---|
| React | UI 组件和应用状态 |
| TypeScript | 静态类型检查 |
| Vite Dev Server | 开发服务和 HMR |
| Vite Build | 模块打包、代码转换和优化 |
| Nginx | HTTP 服务、反向代理、压缩、缓存 |
| Browser | 下载、缓存、解析和执行资源 |
典型生产优化:
React.lazy()
↓
Dynamic Import
↓
Vite Code Splitting
↓
Hashed Static Assets
↓
Nginx gzip / Brotli
↓
Cache-Control
↓
Browser
这是一套适用于 React SPA 的常见生产部署方式。
20. Vite 常见问题
Q1:Vite 是 Web Framework 吗?
不是。
Vite 主要是开发服务器和构建工具。
React 负责 UI;React Router 负责客户端路由;Vite 负责开发和构建。
Q2:Vite 生产环境需要运行 Node.js 吗?
对于普通静态 React SPA,不需要。
Vite 构建生成的静态资源可以直接由 Nginx 或 CDN 提供。
但 SSR 等服务端渲染场景可能需要 Node.js 或其他服务器运行时。
Q3:Vite 会自动开启 gzip 吗?
不会自动让生产服务器启用 gzip。
构建输出中显示的 gzip 体积通常只是压缩体积信息,不代表 Nginx 已启用 gzip。
Q4:Vite 可以自动进行 Code Splitting 吗?
可以。
Vite 的生产构建器会根据模块关系及动态导入等信息生成 Chunk。
但实际拆分结果受到构建器、插件和配置影响。
Q5:Vite 和 Webpack 有什么区别?
两者都能完成前端构建。
Vite 的主要特点是利用原生 ESM 提供快速开发服务,并使用生产构建器优化输出资源。
Webpack 则提供高度可配置的模块打包机制。
二者的实际性能取决于项目规模、配置及所用版本,不能简单认为某个工具在所有情况下都更快。
总结
Vite 的核心知识可以分为四个部分:
| 分类 | 重点 |
|---|---|
| 开发机制 | Dev Server、ESM、HMR、Fast Refresh、Dependency Pre-Bundling |
| 构建优化 | Build、Code Splitting、Tree Shaking、Minification |
| 项目配置 | vite.config.ts、Alias、Proxy、Env、Base、Plugin |
| 生产部署 | dist、Hashed Assets、Source Map、Nginx、Cache-Control |
对于 React + TypeScript SPA 项目,最需要掌握的是:
- 开发服务器与 HMR 的工作方式。
vite.config.ts的常用配置。- 开发代理与生产反向代理的区别。
- 环境变量的构建时注入机制。
- Dynamic Import 与 Code Splitting。
- 静态资源 Hash 和浏览器缓存。
- Vite Build 与 TypeScript 类型检查的区别。