目录
- Vite 简介与基础
- 项目创建与运行
- 依赖管理:package.json 与 lock 文件
- Vite 配置详解
- Vite 代理解决 HTTPS 证书问题
- 常见问题与解决方案
- 开发环境 vs 生产环境
1. Vite 简介与基础
1.1 什么是 Vite?
Vite 是一个 现代前端构建工具,由 Vue.js 作者尤雨溪开发。
核心特点:
- ⚡ 极快的冷启动:使用原生 ESM,无需打包即可启动
- 🔥 即时热更新(HMR) :修改代码立即在浏览器中生效
- 📦 轻量级:核心体积小,按需加载
- 🔌 插件化:丰富的插件生态
- 🎯 多框架支持:React、Vue、Svelte、Lit、Solid、Qwik 等
官方文档:
- 中文:cn.vitejs.dev/
- 英文:vitejs.dev/
1.2 Vite vs Webpack
| 特性 | Vite | Webpack |
|---|---|---|
| 启动速度 | 极快(秒级) | 较慢(需打包) |
| 热更新 | 即时响应 | 较慢 |
| 配置复杂度 | 简单 | 复杂 |
| 生态成熟度 | 新兴,快速增长 | 成熟完善 |
| 适用场景 | 现代项目 | 大型复杂项目 |
1.3 工具链关系
Code
Node.js (JavaScript 运行时)
↓
npm / pnpm / yarn (包管理器)
↓
Vite (构建工具)
↓
React / Vue / Svelte (前端框架)
2. 项目创建与运行
2.1 创建项目
使用 npm
bash
# 创建项目(最新版本)
npm create vite@latest
# 或指定项目名
npm create vite@latest my-vite-app
# 使用 cnpm(淘宝镜像)
cnpm create vite
使用 pnpm(推荐)
bash
pnpm create vite
为什么不需要全局安装 Vite?
npm create vite会临时下载create-vite包- 执行初始化脚本后自动清理
- 避免全局工具导致不同项目版本不一致
2.2 创建流程示例
bash
$ npm create vite@latest
# 交互式选择
✔ Project name: my-vite-app
✔ Select a framework: › React
✔ Select a variant: › TypeScript + SWC
# 进入项目并安装依赖
cd my-vite-app
npm install
# 启动开发服务器
npm run dev
常见模板:
vanilla- 原生 JavaScriptvanilla-ts- 原生 TypeScriptreact- Reactreact-ts- React + TypeScriptreact-swc- React + SWC(更快的编译器)vue- Vue 3vue-ts- Vue 3 + TypeScript
2.3 项目结构
Code
my-vite-app/
├── node_modules/ # 依赖包
├── public/ # 静态资源(不会被打包处理)
│ └── vite.svg
├── src/ # 源代码
│ ├── assets/ # 会被打包的资源
│ ├── components/ # 组件
│ ├── App.tsx
│ └── main.tsx # 入口文件
├── index.html # HTML 入口(注意:在根目录)
├── package.json # 项目配置
├── tsconfig.json # TypeScript 配置
├── vite.config.ts # Vite 配置
└── README.md
重要特点:
index.html在根目录,不在public/(与 Webpack 不同)- Vite 使用
index.html作为入口点
2.4 常用命令
bash
# 安装依赖
npm install
# 启动开发服务器
npm run dev
# 构建生产版本
npm run build
# 预览生产构建
npm run preview
# 查看 Vite 版本
npx vite --version
启动输出示例
bash
$ npm run dev
VITE v5.4.0 ready in 300 ms
➜ Local: http://localhost:5173/
➜ Network: use --host to expose
➜ press h + enter to show help
3. 依赖管理:package.json 与 lock 文件
3.1 package.json
作用:
- 记录项目依赖、版本、脚本和元信息
- 是项目的"身份证"
基本结构:
JSON
{
"name": "my-vite-app",
"private": true,
"version": "0.0.1",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview"
},
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"@types/react": "^18.2.0",
"@types/react-dom": "^18.2.0",
"@vitejs/plugin-react-swc": "^3.5.0",
"typescript": "~5.2.2",
"vite": "^5.4.0"
}
}
dependencies vs devDependencies
| 类型 | 含义 | 示例 | 何时使用 |
|---|---|---|---|
| dependencies | 生产依赖 | react, axios | 运行时需要 |
| devDependencies | 开发依赖 | vite, typescript | 仅开发/构建需要 |
重要提醒:
Code
对于普通项目:区别不大
对于 npm 包:必须严格区分!
错误示例:
npm 包将 vue 写入 dependencies
↓
业务项目已有 vue@3.0.0
↓
安装包后产生两个 Vue 实例
↓
❌ 导致 bug
3.2 版本号规则
版本号格式:主版本.次版本.修订号
Code
示例:1.2.3
│ │ └─ 修订号(Bug 修复)
│ └──── 次版本(新功能,向下兼容)
└─────── 主版本(破坏性变更)
版本号符号
| 符号 | 示例 | 含义 | 安装范围 |
|---|---|---|---|
| 无符号 | 1.2.3 | 固定版本 | 仅 1.2.3 |
| ~ | ~1.2.3 | 波浪号 | 1.2.x(不包括 1.3.0) |
| ^ | ^1.2.3 | 插入号 | 1.x.x(不包括 2.0.0) |
| latest | latest | 最新版本 | 始终最新 |
| ***** | * | 任意版本 | 任何版本 |
示例:
JSON
{
"dependencies": {
"react": "^18.2.0", // 允许 18.x.x
"axios": "~1.6.0", // 允许 1.6.x
"lodash": "4.17.21" // 固定版本
}
}
推荐实践:
- 业务项目:使用
^(次版本更新) - npm 包:使用
~(仅修订号更新) - 关键依赖:使用固定版本
3.3 lock 文件
package-lock.json(npm)
作用:
- 锁定精确版本:记录所有依赖的确切版本
- 加速安装:缓存下载信息
- 保证一致性:团队成员安装相同版本
示例:
JSON
{
"name": "my-vite-app",
"lockfileVersion": 3,
"packages": {
"node_modules/react": {
"version": "18.2.0",
"resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
"integrity": "sha512-..."
}
}
}
工作流程:
Code
首次安装:
npm install
↓
读取 package. json
↓
解析依赖版本范围
↓
下载确切版本
↓
生成 package-lock.json
后续安装:
npm install
↓
优先读取 package-lock.json
↓
直接安装锁定的版本
↓
✅ 速度更快,版本一致
pnpm-lock.yaml(pnpm)
pnpm 的优势:
- 节省磁盘空间:使用硬链接,依赖全局共享
- 严格依赖:避免幽灵依赖(phantom dependencies)
- 速度更快:并行安装
示例:
YAML
lockfileVersion: '6.0'
dependencies:
react:
specifier: ^18.2.0
version: 18.2.0
packages:
/react@18.2.0:
resolution: {integrity: sha512-... }
对比总结
| 工具 | lock 文件 | 特点 | 磁盘占用 |
|---|---|---|---|
| npm | package-lock.json | 成熟稳定 | 每个项目独立 |
| pnpm | pnpm-lock.yaml | 快速节省空间 | 全局共享 |
| cnpm | 不生成 lock | 淘宝镜像加速 | 每个项目独立 |
是否提交 lock 文件到 Git?
Code
✅ 应该提交:
- 业务项目
- 确保团队环境一致
❌ 不提交:
- npm 包(库)
- 让使用者自行解析版本
3.4 常用包管理命令
npm 命令
bash
# 安装所有依赖
npm install
npm i
# 安装生产依赖
npm install <package>
npm i axios
# 安装开发依赖
npm install <package> --save-dev
npm i -D typescript
# 全局安装
npm install -g <package>
# 卸载依赖
npm uninstall <package>
# 更新依赖
npm update <package>
# 查看过时依赖
npm outdated
# 清理缓存
npm cache clean --force
pnpm 命令(推荐)
bash
# 安装所有依赖
pnpm install
pnpm i
# 安装生产依赖
pnpm add <package>
pnpm add axios
# 安装开发依赖
pnpm add -D <package>
pnpm add -D typescript
# 全局安装
pnpm add -g <package>
# 卸载依赖
pnpm remove <package>
# 更新依赖
pnpm update <package>
# 查看依赖树
pnpm list
# 清理未使用的依赖
pnpm prune
对比表
| 操作 | npm | pnpm | yarn |
|---|---|---|---|
| 安装依赖 | npm install | pnpm install | yarn |
| 添加依赖 | npm i pkg | pnpm add pkg | yarn add pkg |
| 开发依赖 | npm i -D pkg | pnpm add -D pkg | yarn add -D pkg |
| 全局安装 | npm i -g pkg | pnpm add -g pkg | yarn global add pkg |
| 卸载依赖 | npm uninstall pkg | pnpm remove pkg | yarn remove pkg |
4. Vite 配置详解
4.1 基础配置文件
文件名:vite.config.ts(推荐)或 vite.config.js
基本结构:
TypeScript
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react-swc'
export default defineConfig({
// 插件配置
plugins: [react()],
// 开发服务器配置
server: {
host: '0.0.0.0',
port: 5173,
},
// 构建配置
build: {
outDir: 'dist',
},
// 路径别名
resolve: {
alias: {
'@': '/src',
},
},
})
4.2 Server 配置(开发服务器)
TypeScript
export default defineConfig({
server: {
// 服务器主机名
host: '0.0.0.0', // 允许局域网访问,默认 'localhost'
// 端口号
port: 5173, // 默认 5173
// 端口被占用时是否自动尝试下一个端口
strictPort: false, // true 则端口被占用时直接退出
// 自动在浏览器中打开应用
open: true, // 或指定路径:'/index.html'
// CORS 配置
cors: true,
// 代理配置(重要!)
proxy: {
'/api': {
target: 'https://localhost:5546',
changeOrigin: true,
secure: false,
}
},
// HMR 配置
hmr: {
overlay: true, // 错误覆盖层
},
// 监听文件变化
watch: {
usePolling: true, // Docker 中可能需要
},
},
})
host 配置说明:
Code
host: 'localhost'
↓
只能本机访问:http://localhost:5173
host: '0.0.0.0'
↓
可以通过局域网访问:
- http://localhost:5173
- http://192.168.1.100:5173
4.3 Build 配置(生产构建)
TypeScript
export default defineConfig({
build: {
// 输出目录
outDir: 'dist',
// 静态资源目录
assetsDir: 'assets',
// 小于此阈值的文件将内联为 base64
assetsInlineLimit: 4096, // 4kb
// 生成 source map
sourcemap: false, // 生产环境建议 false
// 代码分割
rollupOptions: {
output: {
manualChunks: {
'react-vendor': ['react', 'react-dom'],
'router': ['react-router-dom'],
},
},
},
// 压缩配置
minify: 'esbuild', // 或 'terser'
// chunk 大小警告限制(kb)
chunkSizeWarningLimit: 500,
// CSS 代码分割
cssCodeSplit: true,
},
})
4.4 Resolve 配置(路径解析)
TypeScript
import path from 'path'
export default defineConfig({
resolve: {
// 路径别名
alias: {
'@': path.resolve(__dirname, './src'),
'@components': path.resolve(__dirname, './src/components'),
'@utils': path.resolve(__dirname, './src/utils'),
},
// 导入时可以省略的扩展名
extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json'],
},
})
使用示例:
TypeScript
// 不使用别名
import Button from '../../../components/Button'
// 使用别名
import Button from '@components/Button'
TypeScript 配置(tsconfig.json) :
JSON
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["src/*"],
"@components/*": ["src/components/*"]
}
}
}
4.5 Plugins 配置
TypeScript
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react-swc'
import tsconfigPaths from 'vite-tsconfig-paths'
import svgr from 'vite-plugin-svgr'
export default defineConfig({
plugins: [
// React 支持(使用 SWC 编译器,更快)
react(),
// 读取 tsconfig.json 中的 paths 配置
tsconfigPaths(),
// SVG 作为 React 组件导入
svgr(),
],
})
常用插件:
@vitejs/plugin-react- React 支持@vitejs/plugin-react-swc- React + SWC(更快)@vitejs/plugin-vue- Vue 支持vite-tsconfig-paths- TypeScript 路径映射vite-plugin-svgr- SVG 组件化vite-plugin-pwa- PWA 支持
4.6 完整配置示例
TypeScript
import { defineConfig, loadEnv } from 'vite'
import react from '@vitejs/plugin-react-swc'
import tsconfigPaths from 'vite-tsconfig-paths'
import path from 'path'
export default defineConfig(({ mode }) => {
// 加载环境变量
const env = loadEnv(mode, process.cwd(), '')
return {
plugins: [
react(),
tsconfigPaths(),
],
server: {
host: '0.0.0.0',
port: 5173,
open: true,
cors: true,
proxy: {
'/api': {
//env不一定可以加载,可能会报错,直接使用地址即可
target: env.VITE_API_BASE_URL || 'https://localhost:5546',
changeOrigin: true,
secure: false,
rewrite: (path) => path.replace(/^/api/, '/api'),
},
},
},
build: {
outDir: 'dist',
sourcemap: mode === 'development',
rollupOptions: {
output: {
manualChunks: {
'react-vendor': ['react', 'react-dom'],
},
},
},
},
resolve: {
alias: {
'@': path. resolve(__dirname, './src'),
},
},
}
})
5. Vite 代理解决 HTTPS 证书问题
5.1 问题描述
核心问题
开发环境中,后端使用 HTTPS(如 https://localhost:5546),但证书是自签名的,导致前端无法正常访问。
问题表现
Code
❌ 浏览器控制台错误:ERR_CERT_AUTHORITY_INVALID
❌ 前端请求失败,后端没有任何日志记录
❌ 请求被浏览器在客户端直接拦截
问题本质
Code
❌ 常见误解:服务端没有证书
✅ 实际情况:服务端有证书,但证书不被浏览器信任
服务端确实有 SSL 证书:
┌────────────────────────────────┐
│ 颁发给: localhost │
│ 颁发者: localhost (自己) │ ← 自签名,不被信任
│ 有效期: 2024-01-01 ~ 2025-01-01│
└────────────────────────────────┘
浏览器的判断:
"这个证书是 localhost 自己给自己发的,
我怎么知道这是真的 localhost?"
↓
❌ 拒绝连接
5.2 两种访问方式对比
方式 1:直接访问(❌ 有问题)
代码配置:
TypeScript
// request.ts - 直接访问配置
import axios from 'axios';
const request = axios.create({
baseURL: 'https://localhost:5546', // ❌ 直接访问后端
timeout: 10000,
});
export default request;
TypeScript
// 前端调用
fetch('https://localhost:5546/api/users')
网络流程:
Code
┌─────────────┐ ┌─────────────┐
│ 浏览器 │────HTTPS 请求─────▶│ 后端服务 │
│ │ │ : 5546 │
│ 验证证书 ❌ │◀───自签名证书──────│ │
└─────────────┘ └─────────────┘
│
↓
ERR_CERT_AUTHORITY_INVALID
详细流程:
Code
1. 浏览器发起请求
→ https://localhost:5546/api/users
2. TCP 连接建立
→ 浏览器 ↔ localhost: 5546
3. TLS 握手开始
├─ 浏览器 → 后端:ClientHello
└─ 后端 → 浏览器:ServerHello + Certificate
4. 浏览器验证证书
├─ 颁发者:localhost (自签名) ❌
├─ 是否在信任列表:❌ 否
└─ 用户手动例外:❌ 无
5. ❌ 证书验证失败,连接中断
6. ❌ 请求根本没有发送到后端
方式 2:Vite 代理(✅ 解决方案)
代码配置:
TypeScript
// request.ts - 代理配置
import axios from 'axios';
const request = axios.create({
// ✅ 关键:开发环境使用空字符串
baseURL: process.env.NODE_ENV === 'development'
? '' // 开发环境:空字符串,使用 Vite 代理
: 'https://api.production.com', // 生产环境:直接访问
timeout: 10000,
withCredentials: true,
});
export default request;
TypeScript
// vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react-swc'
export default defineConfig({
plugins: [react()],
server: {
host: '0.0.0.0',
port: 5173,
cors: true,
proxy: {
'/api': {
target: 'https://localhost:5546', // 后端地址
changeOrigin: true,
secure: false, // ✅ 关键:忽略证书验证
ws: true, // 支持 WebSocket
}
},
},
})
TypeScript
// 前端调用
fetch('/api/users') // ✅ 相对路径
网络流程:
Code
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 浏览器 │─HTTP─▶│ Vite │─HTTPS─▶│ 后端服务 │
│ │ │ (Node.js) │ │ :5546 │
│ 无证书验证✅│◀─────│ 代理转发 │◀─────│ 自签名证书 │
└─────────────┘ └─────────────┘ └─────────────┘
│
secure: false
(忽略证书验证)
详细流程:
Code
1. 浏览器发起请求
代码:fetch('/api/users')
实际请求:http://localhost:5173/api/users ← HTTP!
2. HTTP 连接(无证书)
浏览器 ↔ Vite (localhost:5173)
├─ 协议:HTTP
├─ 加密:❌ 无
└─ 证书:❌ 不需要
3. Vite 代理匹配
收到请求:/api/users
匹配规则:/api/* → 转发
4. Node.js 发起 HTTPS 请求
目标:https://localhost:5546/api/users
配置:rejectUnauthorized: false ← secure: false 的作用
5. Node.js ↔ 后端 TLS 握手
├─ Node.js → 后端:ClientHello
├─ 后端 → Node.js:ServerHello + Certificate
├─ Node.js:忽略证书验证 ✅ (secure: false)
└─ 握手成功
6. HTTP 请求
Node.js → 后端:GET /api/users
后端 → Node.js:200 OK + 数据
7. Vite 转发响应
Node.js → 浏览器:200 OK + 数据
8. ✅ 请求成功完成
5.3 为什么 Vite 代理能解决问题?
核心原理
Code
问题根源:
浏览器无法忽略 HTTPS 证书验证(安全限制)
Vite 代理的解决思路:
1. 让浏览器不直接接触"不被信任的证书"
2. 浏览器只访问 Vite (HTTP,无证书问题)
3. 让 Node.js 去处理"不被信任的证书"
4. Node.js 可以配置"我不在乎证书是否被信任"
详细解析
1. 协议隔离
Code
浏览器看到的:
http://localhost:5173/api/users ← HTTP
├─ HTTP 不需要证书验证
├─ 浏览器不会进行 TLS 握手
└─ 没有证书相关的安全检查
Node.js 处理的:
https://localhost:5546/api/users ← HTTPS
├─ Node.js 可以配置 secure: false
├─ 等价于 rejectUnauthorized: false
└─ 接受任何证书,包括自签名
2. 验证主体转换
Code
直接访问:
浏览器验证证书
├─ 严格的安全策略
├─ 无法配置忽略证书
└─ 自签名证书被拒绝 ❌
Vite 代理:
Node.js 验证证书
├─ 开发者完全控制
├─ 可以配置忽略证书
└─ 自签名证书被接受 ✅
3. secure: false 的实际作用
JavaScript
// Vite 配置
export default defineConfig({
server: {
proxy: {
'/api': {
target: 'https://localhost:5546',
secure: false, // ← 这个配置
}
},
},
})
等价于 Node.js 代码:
JavaScript
const https = require('https');
const agent = new https.Agent({
rejectUnauthorized: false, // ← secure: false 的作用
});
https.get('https://localhost:5546/api/users', { agent }, (res) => {
// ✅ 成功连接,即使证书是自签名的
});
为什么浏览器不能这样做?
Code
浏览器的限制:
❌ 不提供 rejectUnauthorized 选项
❌ 必须严格验证证书
❌ 保护普通用户的安全
Node.js 的灵活性:
✅ 提供 rejectUnauthorized 选项
✅ 开发者完全控制
✅ 适合开发环境的灵活性
5.4 关键差异对比
| 维度 | 直接访问 | Vite 代理 |
|---|---|---|
| 浏览器请求目标 | https://localhost:5546 | http://localhost:5173 |
| 协议 | HTTPS | HTTP |
| 证书验证 | 浏览器验证 | Node.js 处理 |
| 证书问题 | ❌ 严格验证,拒绝自签名 | ✅ 可配置忽略 |
| 请求是否到达后端 | ❌ 被浏览器拦截 | ✅ 正常到达 |
| 配置复杂度 | 简单,但有问题 | 稍复杂,但完美解决 |
| 生产环境 | 需要真实证书 | 代理不存在,直接访问 |
5.5 前端页面的协议影响
关键理解
Code
前端页面的协议:
├─ https://localhost:5173 (前端用 HTTPS 发布)
└─ http://localhost:5173 (前端用 HTTP 发布)
↓
都不影响 Vite 代理的工作方式
↓
浏览器 → Vite 的连接始终是:
http://localhost:5173/api/xxx ← 始终是 HTTP!
验证示例:
TypeScript
// 即使前端页面是 HTTPS
// 访问:https://localhost:5173
// JavaScript 代码
fetch('/api/users')
// 实际请求仍然是
// http://localhost:5173/api/users ← HTTP!
开发者工具验证:
Code
Network 标签显示:
┌─────────────────────────────────────┐
│ Request URL: http://localhost:5173 │ ← HTTP
│ (即使前端页面是 HTTPS) │
└─────────────────────────────────────┘
5.6开发时需要代理,生产时不需要
为什么开发时需要代理?
-
开发时的运行环境:
-
前端:Vite 启动一个开发服务器(e.g.,
localhost:3000),它是热重载的 Node.js 服务器,专门为开发优化。代码是动态编译的,不是静态文件。 -
后端:ABP 或 ASP.NET Core 应用运行在另一个进程(e.g.,
localhost:7161),可能是 HTTPS 的(需要证书)。 -
问题
:两个服务器运行在
不同端口
,浏览器从
localhost:3000访问前端,但前端代码需要调用后端 API(e.g.,
fetch('/api/test'))。这就形成了“跨域”:
- 浏览器安全策略:不同端口/协议被视为不同“域”,直接请求
https://localhost:7161/api/test会报 CORS 错误。 - HTTPS 证书:后端用自签名证书,浏览器不信任,会弹出警告或拒绝。
- 浏览器安全策略:不同端口/协议被视为不同“域”,直接请求
-
代理的作用:Vite 代理作为“中间人”,将前端的
/api请求转发到后端。代理在服务器端运行,能绕过浏览器的限制(忽略证书,处理跨域)。
-
-
同一个电脑的“隔离” :
- 虽然是同一台电脑,但前端和后端是不同的进程/服务,就像两个独立的程序。即使端口不同,也会被浏览器视为“跨域”。
- 如果不代理,你得手动配置后端允许 CORS(e.g., 在 ABP 中添加
AllowAnyOrigin),或让前端直接访问后端端口——但这复杂且不安全(开发时 HTTPS 证书问题)。
-
类比:想象你在家开发:客厅(前端服务器)和厨房(后端服务器)是同一栋房子,但你不能直接从客厅拿厨房的东西,得用“传送带”(代理)。
为什么生产时不需要代理?
-
生产时的运行环境:
- 前端:Vite 打包成静态文件(HTML/CSS/JS),放在服务器(如 IIS)的目录里。这些是“死”文件,不需要开发服务器。
- 后端:ABP 应用也部署在同一服务器上,IIS 或 Nginx 直接路由请求。
- 统一部署:一切都在
https://yourdomain.com下,前端请求/api/test时,服务器(IIS)直接转发到后端代码,无需额外代理。 - 无跨域:因为都在同一域,浏览器不触发 CORS 或证书问题。
-
代理不是生产必需:
- 代理是 Vite 的开发工具,是为了方便开发者(热重载、快速迭代)。生产时,服务器本身处理这些问题。
- 如果生产时还用代理,会增加复杂性(e.g., Nginx 配置),且不必要。
总结:开发时代理是为了“便利”,生产时服务器“接棒”
- 核心差异:开发时,前后端是分离的动态服务;生产时,是静态文件 + 服务器应用的统一体。
- 疑惑的来源:很多人以为“同一个电脑”就不需要代理,但其实是进程和端口的隔离导致的。代理让开发更顺畅,避免你花时间配置 CORS/证书。
- 建议:接受这个设计,它是现代前端工具(如 Vite)的标准做法。如果你想验证,可以试试不配置代理,直接在前端代码中用
fetch('https://localhost:7161/api/test')——你会看到 CORS 和证书错误!
6. 常见问题与解决方案
6.1 Network: use --host to expose
问题描述
bash
$ npm run dev
VITE v5.4.0 ready in 300 ms
➜ Local: http://localhost:5173/
➜ Network: use --host to expose ← 无法通过局域网访问
解决方案
方法 1:修改 vite.config.ts(推荐)
TypeScript
export default defineConfig({
server: {
host: '0.0.0.0', // ← 添加这行
port: 5173,
},
})
方法 2:命令行参数
bash
npm run dev -- --host
# 或
vite --host
方法 3:修改 package.json
JSON
{
"scripts": {
"dev": "vite --host"
}
}
效果对比
Code
修改前:
➜ Local: http://localhost:5173/
➜ Network: use --host to expose
修改后:
➜ Local: http://localhost:5173/
➜ Network: http://192.168.1.100:5173/ ← 可以通过局域网访问
6.2 端口被占用
问题描述
bash
Port 5173 is in use, trying another one...
➜ Local: http://localhost:5174/ ← 自动使用下一个端口
解决方案
方法 1:指定端口
TypeScript
export default defineConfig({
server: {
port: 3000, // 使用其他端口
},
})
方法 2:强制使用指定端口(被占用时退出)
TypeScript
export default defineConfig({
server: {
port: 5173,
strictPort: true, // 端口被占用时不尝试其他端口,直接退出
},
})
方法 3:查找并杀死占用端口的进程
bash
# Windows
netstat -ano | findstr :5173
taskkill /PID <进程ID> /F
# macOS/Linux
lsof -i :5173
kill -9 <进程ID>
6.3 代理不工作
问题描述
Code
前端请求:fetch('/api/users')
实际访问:http://localhost:5173/api/users
结果:404 Not Found
排查清单
1. 检查 request.ts 配置
TypeScript
// ❌ 错误:baseURL 设置了完整 URL
const request = axios.create({
baseURL: 'https://localhost:5546', // 绕过了代理
});
// ✅ 正确:baseURL 为空字符串
const request = axios.create({
baseURL: process.env.NODE_ENV === 'development' ? '' : 'https://api.prod.com',
});
2. 检查 vite.config.ts 配置
TypeScript
export default defineConfig({
server: {
proxy: {
'/api': {
target: 'https://localhost:5546', // ✅ 目标地址
changeOrigin: true, // ✅ 必须
secure: false, // ✅ HTTPS 自签名证书需要
}
},
},
})
3. 检查请求路径
TypeScript
// ❌ 错误:绝对路径
fetch('https://localhost:5546/api/users')
// ✅ 正确:相对路径
fetch('/api/users')
4. 重启开发服务器
bash
# 修改 vite.config.ts 后必须重启
Ctrl+C
npm run dev
6.4 CORS 跨域问题
问题描述
Code
Access to fetch at 'https://localhost:5546/api/users' from origin 'http://localhost:5173'
has been blocked by CORS policy
解决方案
方案 1:使用 Vite 代理(推荐)
TypeScript
export default defineConfig({
server: {
proxy: {
'/api': {
target: 'https://localhost:5546',
changeOrigin: true, // ← 自动处理跨域
}
},
},
})
方案 2:后端配置 CORS(如果不用代理)
C#
// ASP.NET Core - Program.cs
builder.Services.AddCors(options => {
options.AddDefaultPolicy(policy => {
policy.WithOrigins("http://localhost:5173")
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials();
});
});
app.UseCors();
6.5 环境变量不生效
问题描述
TypeScript
// .env.development
VITE_API_BASE_URL=https://localhost:5546
// 代码中
console.log(import.meta.env.VITE_API_BASE_URL) // undefined
解决方案
1. 环境变量必须以 VITE_ 开头
bash
# ❌ 错误
API_BASE_URL=https://localhost:5546
# ✅ 正确
VITE_API_BASE_URL=https://localhost:5546
2. 重启开发服务器
bash
# 修改 . env 文件后必须重启
Ctrl+C
npm run dev
3. 使用 loadEnv 函数
TypeScript
import { defineConfig, loadEnv } from 'vite'
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process. cwd(), '')
return {
server: {
proxy: {
'/api': {
target: env.VITE_API_BASE_URL,
}
},
},
}
})
6.6 TypeScript 路径别名不生效
问题描述
TypeScript
import Button from '@/components/Button' // 报错:找不到模块
解决方案
1. 配置 vite.config.ts
TypeScript
import path from 'path'
export default defineConfig({
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
},
},
})
2. 配置 tsconfig.json
JSON
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
}
}
}
3. 安装类型定义(如果使用 Node.js API)
bash
npm install -D @types/node
6.7 热更新(HMR)失效
可能原因与解决方案
1. Docker / WSL2 环境
TypeScript
export default defineConfig({
server: {
watch: {
usePolling: true, // 使用轮询而不是文件系统事件
},
},
})
2. 文件路径过长
Code
Windows 系统文件路径不要超过 260 字符
3. 文件在 node_modules 中
Code
node_modules 中的文件不会触发热更新
4. 浏览器缓存
Code
按 Ctrl+Shift+R 强制刷新
或在开发者工具中禁用缓存
7. 开发环境 vs 生产环境
7.1 环境对比
| 维度 | 开发环境 | 生产环境 |
|---|---|---|
| 命令 | npm run dev | npm run build |
| Vite 服务器 | ✅ 运行 | ❌ 不存在 |
| 代理功能 | ✅ 启用 | ❌ 不存在 |
| 代码 | 源代码 | 打包后的代码 |
| 速度 | 快速启动 | 优化后的静态文件 |
| 热更新 | ✅ 支持 | ❌ 不适用 |
| 文件 | 内存中 | dist/ 目录 |
7.2 开发环境流程
Code
npm run dev
↓
Vite 开发服务器启动(内存中)
↓
访问:http://localhost:5173
↓
浏览器请求 /api/users
↓
实际请求:http://localhost:5173/api/users
↓
Vite 代理拦截
↓
转发到:https://localhost:5546/api/users
↓
Node.js 处理证书问题(secure: false)
↓
后端响应 → Vite → 浏览器
↓
✅ 开发顺利进行
7.3 生产环境流程
Code
npm run build
↓
Vite 打包项目
↓
生成 dist/ 目录
├── index.html
├── assets/
│ ├── index-abc123.js
│ └── index-def456.css
└── favicon.ico
↓
部署到生产服务器
↓
访问:https://your-app.com
↓
浏览器请求 /api/users
↓
实际请求:https://your-app.com/api/users
↓
没有 Vite 代理!直接访问后端
↓
后端使用权威 CA 颁发的证书(Let's Encrypt等)
↓
浏览器信任证书 ✅
↓
✅ 生产环境正常运行
7.4 配置区分
request.ts 配置
TypeScript
import axios from 'axios';
const request = axios.create({
baseURL: process.env.NODE_ENV === 'development'
? '' // ✅ 开发环境:空字符串,使用 Vite 代理
: import.meta.env.VITE_API_BASE_URL || 'https://api.production.com', // 生产环境
timeout: 10000,
withCredentials: true,
});
export default request;
环境变量配置
.env.development (开发环境)
bash
VITE_API_BASE_URL=
NODE_ENV=development
.env.production (生产环境)
bash
VITE_API_BASE_URL=https://api.your-domain.com
NODE_ENV=production
vite.config.ts 配置
TypeScript
import { defineConfig, loadEnv } from 'vite'
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process. cwd(), '')
return {
// 开发服务器配置(仅开发环境)
server: {
proxy: {
'/api': {
target: env.VITE_DEV_SERVER_URL || 'https://localhost:5546',
changeOrigin: true,
secure: false,
}
},
},
// 构建配置(生产环境)
build: {
outDir: 'dist',
sourcemap: mode === 'development',
},
}
})
vite-env.d.ts 文件说明
/// <reference types="vite/client" />
这个文件是 TypeScript 类型定义文件,用于:
- 为 Vite 客户端提供 TypeScript 类型支持
- 识别 Vite 特定的环境变量(如
import.meta.env) - 提供开发工具的类型提示和自动完成
能否删除?
不建议删除,原因如下:
- 类型检查会报错 - 删除后,使用
import.meta.env等 Vite 特性时会出现类型错误 - IDE 支持变差 - 会失去自动完成和类型提示
- 项目配置文件 - 这是 Vite + React + TypeScript 项目的标准配置文件
如果确实想删除
只有在以下情况下才能安全删除:
- 项目不使用 TypeScript
- 或者你在 tsconfig.json 中已有其他类型定义配置
建议保留此文件 以获得最佳的开发体验。
7.5 为什么生产环境不需要代理?
Code
生产环境的证书是可信的:
开发环境证书:
┌────────────────────────────────┐
│ 颁发给: localhost │
│ 颁发者: localhost (自己) ❌ │ ← 自签名,不被信任
└────────────────────────────────┘
需要 Vite 代理绕过证书验证
生产环境证书:
┌────────────────────────────────┐
│ 颁发给: your-app.com │
│ 颁发者: Let's Encrypt ✅ │ ← 权威 CA,被信任
└────────────────────────────────┘
浏览器直接信任,不需要代理
7.6 部署清单
构建前检查:
Code
□ 环境变量配置正确 (. env.production)
□ API 地址指向生产服务器
□ baseURL 配置正确
□ 生产环境后端使用权威 CA 证书
□ CORS 配置允许生产域名
构建命令:
bash
npm run build
部署文件:
Code
dist/
├── index.html # 入口 HTML
├── assets/ # 静态资源
│ ├── index-[hash].js
│ └── index-[hash].css
└── favicon.ico
常见部署方式:
- Nginx:将
dist/目录作为静态文件服务 - Vercel / Netlify:自动构建和部署
- Docker:打包成镜像部署
- CDN:上传到 OSS + CDN 加速
8. 总结与最佳实践
8.1 核心要点
Code
1. Vite 代理的本质:
├─ 浏览器 → Vite (HTTP,无证书)
├─ Vite → 后端 (HTTPS,忽略证书)
└─ 隔离了浏览器和后端的直接连接
2. 为什么能解决证书问题:
├─ 浏览器不直接访问 HTTPS 后端
├─ Node.js 可以配置忽略证书验证
└─ secure: false = rejectUnauthorized: false
3. 开发环境 vs 生产环境:
├─ 开发:Vite 代理 + 自签名证书
└─ 生产:直接访问 + 权威 CA 证书
4. 配置关键点:
├─ request.ts: baseURL 开发环境为空字符串
├─ vite.config.ts: secure: false
└─ 修改配置后必须重启服务器
8.2 开发最佳实践
1. 项目初始化
bash
# 使用 pnpm(推荐)
pnpm create vite
# 选择 TypeScript 模板
✔ Select a framework: › React
✔ Select a variant: › TypeScript + SWC
2. 配置文件结构
Code
project/
├── . env.development # 开发环境变量
├── .env.production # 生产环境变量
├── vite.config.ts # Vite 配置
├── tsconfig.json # TypeScript 配置
├── package.json # 依赖管理
└── src/
├── utils/
│ └── request.ts # 请求配置
└── ...
3. 标准 request.ts
TypeScript
import axios from 'axios';
const request = axios. create({
baseURL: process.env.NODE_ENV === 'development' ? '' : import.meta.env.VITE_API_BASE_URL,
timeout: 10000,
withCredentials: true,
});
// 请求拦截器
request.interceptors.request.use(
(config) => {
const token = localStorage.getItem('token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
(error) => Promise.reject(error)
);
// 响应拦截器
request.interceptors.response. use(
(response) => response. data,
(error) => {
console.error('请求失败:', error);
return Promise.reject(error);
}
);
export default request;
4. 标准 vite.config.ts
TypeScript
import { defineConfig, loadEnv } from 'vite'
import react from '@vitejs/plugin-react-swc'
import tsconfigPaths from 'vite-tsconfig-paths'
import path from 'path'
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd(), '')
return {
plugins: [
react(),
tsconfigPaths(),
],
server: {
host: '0.0.0.0',
port: 5173,
open: true,
cors: true,
proxy: {
'/api': {
target: env.VITE_DEV_SERVER_URL || 'https://localhost:5546',
changeOrigin: true,
secure: false,
rewrite: (path) => path.replace(/^/api/, '/api'),
},
},
},
build: {
outDir: 'dist',
sourcemap: mode === 'development',
rollupOptions: {
output: {
manualChunks: {
'react-vendor': ['react', 'react-dom', 'react-router-dom'],
},
},
},
},
resolve: {
alias: {
'@': path. resolve(__dirname, './src'),
},
},
}
})
5. Git 管理
bash
# .gitignore
node_modules/
dist/
. env. local
.env.*. local
# 提交到 Git
. env.development # ✅ 提交
.env.production # ✅ 提交
package-lock.json # ✅ 提交
pnpm-lock.yaml # ✅ 提交
8.3 常用命令速查
bash
# 项目创建
pnpm create vite
# 依赖安装
pnpm install
# 开发启动
pnpm dev
# 生产构建
pnpm build
# 预览构建
pnpm preview
# 添加依赖
pnpm add <package>
# 添加开发依赖
pnpm add -D <package>
# 更新依赖
pnpm update
# 清理缓存
pnpm store prune
8.4 学习资源
官方文档:
- Vite: cn.vitejs.dev/
- Vite 插件: vitejs.dev/plugins/
相关技术:
- React: react.dev/
- TypeScript: www.typescriptlang.org/
- pnpm: pnpm.io/zh/
推荐阅读:
附录
路径优化插件
它的作用就是: 自动读取你的 tsconfig.json 或 jsconfig.json 里的路径别名配置,并在 Vite 中生效,无需手动在 vite.config.ts 中写 alias!
效果:
import MainRouter from "../../router/MainRouter.ts";
import MainRouter from "@/router/MainRouter.ts";
- 安装依赖
npm install vite-tsconfig-paths --save-dev
- 配置 vite.config.ts
// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import tsconfigPaths from 'vite-tsconfig-paths';
export default defineConfig({
plugins: [
react(),
tsconfigPaths(), // 👈 自动读取 tsconfig.json 中的 paths
],
});
- 设置 tsconfig.json 中的路径别名
// tsconfig.app.json
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
}
}
}
优点
- 不需要手动写
resolve.alias。 - 支持多个别名,如
"@components/*": ["src/components/*"]。 - 和
tsc/ IDE / ESLint / Jest 保持一致性。
问题:前端请求后端失败
Code
1. 检查浏览器控制台
├─ ERR_CERT_AUTHORITY_INVALID → 证书问题,配置代理
├─ 404 Not Found → 后端未启动或路径错误
├─ CORS error → 跨域问题,配置代理或后端 CORS
└─ Network error → 网络问题或后端未启动
2. 检查 request.ts
├─ baseURL 开发环境是否为空字符串
└─ 请求路径是否使用相对路径
3. 检查 vite.config.ts
├─ proxy 配置是否正确
├─ target 地址是否正确
├─ secure: false 是否配置(HTTPS 后端)
└─ 修改后是否重启服务器
4. 检查后端
├─ 后端是否启动
├─ 端口是否正确
├─ API 路径是否正确
└─ 后端日志是否有记录
问题:无法通过局域网访问
Code
1. 配置 server.host
server: {
host: '0.0.0.0',
}
2. 检查防火墙
├─ Windows: 允许 5173 端口
└─ 路由器: 端口映射配置
3. 重启服务器
Ctrl+C → npm run dev
问题:热更新不生效
Code
1. Docker/WSL2 环境
server: {
watch: { usePolling: true },
}
2. 清除缓存
├─ 浏览器:Ctrl+Shift+R
└─ Vite: 删除 node_modules/. vite
3. 检查文件路径
└─ 不要超过 260 字符(Windows)