前端开发中的跨域问题:Vite 开发环境配置指南

前端开发中的跨域问题:Vite 开发环境配置指南

前言

在前端开发过程中,跨域问题是一个经常遇到的挑战。当你的前端应用运行在 http://localhost:5173,而后端 API 运行在 http://2.x.x.x:28xxx 时,浏览器会阻止这种跨域请求。本文将详细介绍如何使用 Vite 的代理功能来解决开发环境中的跨域问题。

什么是跨域?

跨域(Cross-Origin Resource Sharing,CORS)是浏览器的同源策略限制。当以下三个要素中任意一个不同时,就会产生跨域:

  • 协议 (Protocol):如 httphttps
  • 域名 (Domain):如 localhost2.x.x.x
  • 端口 (Port):如 517328xxx

当浏览器检测到跨域请求时,会抛出类似以下的错误:

csharp 复制代码
Access to XMLHttpRequest at 'http://2.x.x.x:28xxx/login' from origin 'http://localhost:5173' 
has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

解决方案对比

1. 后端配置 CORS(生产环境推荐)

在后端服务器设置响应头,允许指定的源访问:

javascript 复制代码
// 后端示例(Node.js/Express)
app.use(cors({
  origin: 'http://localhost:5173',
  credentials: true
}));

优点 :符合生产环境最佳实践
缺点:需要后端配合,开发环境可能无法修改后端

2. 浏览器插件(不推荐)

使用 Chrome 插件禁用 CORS 检查。

优点 :快速
缺点:仅适用于开发,不安全,不推荐

3. Vite 代理(开发环境推荐)⭐

利用 Vite 开发服务器的代理功能,将请求转发到后端服务器。

优点

  • 无需修改后端代码
  • 开发环境专用,不影响生产环境
  • 配置简单,功能强大
  • 可以处理路径重写、请求头修改等

Vite 代理配置详解

基础配置

vite.config.ts 中配置 server.proxy

typescript 复制代码
import { defineConfig } from 'vite'

export default defineConfig({
  server: {
    // 端口号
    port: 5173,
    // 允许外部访问(局域网访问)
    host: "0.0.0.0",
    // 代理配置
    proxy: {
      // 代理所有以 /api 开头的请求
      "/api": {
        // 目标服务器地址
        target: "http://后端地址",
        // 改变请求头中的 origin,确保后端能正确识别来源
        changeOrigin: true,
        // 路径重写:去掉 /api 前缀
        rewrite: (path) => path.replace(/^\/api/, "")
      }
    }
  }
})

配置项说明

target
  • 类型string
  • 说明:后端服务器的实际地址
  • 示例"http://后端地址"
changeOrigin
  • 类型boolean
  • 默认值false
  • 说明 :是否改变请求头中的 origin 字段
  • 作用 :当设置为 true 时,请求头中的 origin 会被设置为 target 的值,这样可以避免后端因 origin 不匹配而拒绝请求
  • 注意:此时更改http中的baseURL为'/api'
rewrite
  • 类型(path: string) => string
  • 说明:路径重写函数
  • 作用:可以修改请求路径,例如去掉前缀、添加前缀等

示例

typescript 复制代码
rewrite: (path) => path.replace(/^\/api/, "")
// 请求 /api/login → 实际请求 http://后端地址/login

完整配置示例

typescript 复制代码
// vite.config.ts
import { defineConfig, loadEnv } from 'vite'

export default defineConfig(({ mode }) => {
  const env = loadEnv(mode, process.cwd())
  
  return {
    server: {
      port: Number(env.VITE_PORT) || 5173,
      host: "0.0.0.0",
      proxy: {
        "/api": {
          target: env.VITE_API_BASE_URL || "后端地址",
          changeOrigin: true,
          rewrite: (path) => path.replace(/^\/api/, ""),
          // 可选:配置 WebSocket 代理
          ws: true,
          // 可选:自定义请求头
          configure: (proxy, options) => {
            proxy.on('proxyReq', (proxyReq, req, res) => {
              // 可以在这里修改请求头
              console.log('代理请求:', req.url)
            })
          }
        }
      },
      // CORS 配置(可选,通常不需要)
      cors: {
        origin: "*",
        credentials: true
      }
    }
  }
})

前端 HTTP 客户端配置

配置好代理后,需要在前端 HTTP 客户端中设置正确的 baseURL

Axios 配置示例

typescript 复制代码
// src/utils/http/index.ts
import Axios, { type AxiosRequestConfig } from 'axios'

const defaultConfig: AxiosRequestConfig = {
  timeout: 20000,
  // 使用相对路径,Vite 会自动代理到后端
  baseURL: "/api",
  headers: {
    Accept: "application/json, text/plain, */*",
    "Content-Type": "application/json",
    "X-Requested-With": "XMLHttpRequest"
  }
}

const axiosInstance = Axios.create(defaultConfig)

// 使用示例
export const http = {
  get: <T>(url: string, config?: AxiosRequestConfig) => {
    return axiosInstance.get<T>(url, config)
  },
  post: <T>(url: string, data?: any, config?: AxiosRequestConfig) => {
    return axiosInstance.post<T>(url, data, config)
  }
}

请求流程

  1. 前端发起请求

    typescript 复制代码
    http.post("/login", { username: "admin", password: "123456" })
  2. 实际请求路径

    bash 复制代码
    http://localhost:5173/api/login
  3. Vite 代理转发

    bash 复制代码
    http://后端地址/login  (去掉了 /api 前缀)
  4. 后端响应

    复制代码
    响应原路返回 → Vite 代理 → 前端

高级配置场景

1. 多个代理规则

当需要代理多个不同的后端服务时:

typescript 复制代码
proxy: {
  "/api": {
    target: "http://api.example.com",
    changeOrigin: true,
    rewrite: (path) => path.replace(/^\/api/, "")
  },
  "/upload": {
    target: "http://upload.example.com",
    changeOrigin: true
  }
}

2. 使用正则表达式匹配

typescript 复制代码
proxy: {
  // 匹配所有以 /api 或 /v1 开头的请求
  "^/api|^/v1": {
    target: "http://2.2.50.2:28082",
    changeOrigin: true
  }
}

3. 代理 WebSocket

typescript 复制代码
proxy: {
  "/api": {
    target: "ws://后端地址",
    ws: true,  // 启用 WebSocket 代理
    changeOrigin: true
  }
}

4. 自定义请求头

typescript 复制代码
proxy: {
  "/api": {
    target: "http://后端地址",
    changeOrigin: true,
    configure: (proxy, options) => {
      proxy.on('proxyReq', (proxyReq, req, res) => {
        // 添加自定义请求头
        proxyReq.setHeader('X-Custom-Header', 'custom-value')
      })
    }
  }
}

常见问题排查

1. 代理不生效

检查项

  • ✅ 确认 vite.config.ts 配置正确
  • ✅ 重启 Vite 开发服务器
  • ✅ 检查 baseURL 是否以 /api 开头
  • ✅ 查看浏览器 Network 面板,确认请求是否发送到正确的地址

2. 请求 404

可能原因

  • rewrite 配置不正确,路径被错误重写
  • target 地址不正确
  • 后端路由不存在

解决方案

typescript 复制代码
// 添加日志查看实际请求路径
configure: (proxy, options) => {
  proxy.on('proxyReq', (proxyReq, req, res) => {
    console.log('代理请求路径:', proxyReq.path)
  })
}

原因:跨域请求默认不携带 Cookie

解决方案

typescript 复制代码
proxy: {
  "/api": {
    target: "http://2.2.50.2:28082",
    changeOrigin: true,
    // 确保 Cookie 被正确传递
    cookieDomainRewrite: "",
    cookiePathRewrite: ""
  }
}

4. 生产环境配置

重要:Vite 的代理功能仅在开发环境生效,生产环境需要:

  1. 使用 Nginx 反向代理

    nginx 复制代码
    location /api {
        proxy_pass http://2.2.50.2:28082;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
  2. 或后端配置 CORS

    javascript 复制代码
    // 允许所有源(生产环境建议指定具体域名)
    app.use(cors({
      origin: process.env.ALLOWED_ORIGINS?.split(',') || '*',
      credentials: true
    }))

最佳实践

1. 使用环境变量

typescript 复制代码
// .env.development
VITE_API_BASE_URL=http://后端地址
VITE_PORT=5173

// vite.config.ts
export default defineConfig(({ mode }) => {
  const env = loadEnv(mode, process.cwd())
  
  return {
    server: {
      proxy: {
        "/api": {
          target: env.VITE_API_BASE_URL,
          changeOrigin: true,
          rewrite: (path) => path.replace(/^\/api/, "")
        }
      }
    }
  }
})

2. 类型安全

typescript 复制代码
// 定义 API 响应类型
export interface ApiResponse<T> {
  code: number
  msg: string
  data: T
}

// 使用示例
const response = await http.post<ApiResponse<UserInfo>>("/login", {
  username: "admin",
  password: "123456"
})

3. 错误处理

typescript 复制代码
axiosInstance.interceptors.response.use(
  (response) => response.data,
  (error) => {
    if (error.response) {
      // 服务器返回错误
      console.error('服务器错误:', error.response.status)
    } else if (error.request) {
      // 请求已发送但没有收到响应
      console.error('网络错误:', error.request)
    } else {
      // 请求配置错误
      console.error('请求错误:', error.message)
    }
    return Promise.reject(error)
  }
)

总结

Vite 的代理功能是解决开发环境跨域问题的最佳方案:

无需修改后端代码

配置简单,功能强大

仅影响开发环境,不影响生产环境

支持路径重写、请求头修改等高级功能

通过合理配置 Vite 代理,可以大大提升前端开发效率,避免跨域问题带来的困扰。

参考文档


希望这篇文章能帮助你解决跨域问题!如果还有疑问,欢迎在评论区讨论。 🚀

相关推荐
kuromiluu2 小时前
从原理到实践:Vite
vite
可别3903 小时前
使用Worker打包报错
前端·vue.js
风中凌乱的L3 小时前
vue canvas标注
前端·vue.js·canvas
掘金014 小时前
在 Vue 3 项目中使用 MQTT 获取数据
前端·javascript·vue.js
QuantumLeap丶4 小时前
《uni-app跨平台开发完全指南》- 03 - Vue.js基础入门
前端·vue.js·uni-app
一 乐4 小时前
个人理财系统|基于java+小程序+APP的个人理财系统设计与实现(源码+数据库+文档)
java·前端·数据库·vue.js·后端·小程序
网络点点滴4 小时前
reactive创建对象类型的响应式数据
前端·javascript·vue.js
熊猫比分站4 小时前
[特殊字符] Java/Vue 实现体育比分直播系统,支持多端实时更新
java·开发语言·vue.js
多则惑少则明6 小时前
Vue开发系列——自定义组件开发
前端·javascript·vue.js