Vite的使用

4 阅读16分钟

目录

  1. Vite 简介与基础
  2. 项目创建与运行
  3. 依赖管理:package.json 与 lock 文件
  4. Vite 配置详解
  5. Vite 代理解决 HTTPS 证书问题
  6. 常见问题与解决方案
  7. 开发环境 vs 生产环境

1. Vite 简介与基础

1.1 什么是 Vite?

Vite 是一个 现代前端构建工具,由 Vue.js 作者尤雨溪开发。

核心特点

  • 极快的冷启动:使用原生 ESM,无需打包即可启动
  • 🔥 即时热更新(HMR) :修改代码立即在浏览器中生效
  • 📦 轻量级:核心体积小,按需加载
  • 🔌 插件化:丰富的插件生态
  • 🎯 多框架支持:React、Vue、Svelte、Lit、Solid、Qwik 等

官方文档

1.2 Vite vs Webpack

特性ViteWebpack
启动速度极快(秒级)较慢(需打包)
热更新即时响应较慢
配置复杂度简单复杂
生态成熟度新兴,快速增长成熟完善
适用场景现代项目大型复杂项目

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 - 原生 JavaScript
  • vanilla-ts - 原生 TypeScript
  • react - React
  • react-ts - React + TypeScript
  • react-swc - React + SWC(更快的编译器)
  • vue - Vue 3
  • vue-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
latestlatest最新版本始终最新
******任意版本任何版本

示例

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)

作用

  1. 锁定精确版本:记录所有依赖的确切版本
  2. 加速安装:缓存下载信息
  3. 保证一致性:团队成员安装相同版本

示例

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 文件特点磁盘占用
npmpackage-lock.json成熟稳定每个项目独立
pnpmpnpm-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

对比表

操作npmpnpmyarn
安装依赖npm installpnpm installyarn
添加依赖npm i pkgpnpm add pkgyarn add pkg
开发依赖npm i -D pkgpnpm add -D pkgyarn add -D pkg
全局安装npm i -g pkgpnpm add -g pkgyarn global add pkg
卸载依赖npm uninstall pkgpnpm remove pkgyarn 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:5546http://localhost:5173
协议HTTPSHTTP
证书验证浏览器验证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 devnpm 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
  • 提供开发工具的类型提示和自动完成

能否删除?

不建议删除,原因如下:

  1. 类型检查会报错 - 删除后,使用 import.meta.env 等 Vite 特性时会出现类型错误
  2. IDE 支持变差 - 会失去自动完成和类型提示
  3. 项目配置文件 - 这是 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-tsconfig-paths

它的作用就是: 自动读取你的 tsconfig.jsonjsconfig.json 里的路径别名配置,并在 Vite 中生效,无需手动在 vite.config.ts 中写 alias

效果:
import MainRouter from "../../router/MainRouter.ts";
import MainRouter from "@/router/MainRouter.ts";
  1. 安装依赖
npm install vite-tsconfig-paths --save-dev
  1. 配置 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
  ],
});
  1. 设置 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)