Skip to content

开发代理(科普 + 命令)

一、为什么需要代理

前端跑在 localhost:3100,后端在 192.168.3.240:81。浏览器同源策略会拦截这种跨域请求。解决思路有三条路,复杂度递进:

浏览器 ──① 直发──> 后端地址(需后端开 CORS)
浏览器 ──② 代理──> localhost:3100/dev-api ──> 开发服务器转发 ──> 后端
浏览器 ──③ 免重启代理──> 改配置刷新即切换后端(无需重启 dev server)

二、/dev-api 前缀是哪来的(先看这个)

/dev-api 不是后端真实路径,而是前端约定 + 代理规则 + 构建替换三者配合出来的"暗号":

  1. 前端写的是假路径:业务代码里请求写成 request('/dev-api/user/info')/dev-api 只是个约定前缀,用来标记"这条请求要走代理",不代表任何真实后端路径。
  2. 代理靠它识别要转发的请求:开发服务器代理规则 proxy: { "/dev-api": {...} } 表示"所有以 /dev-api 开头的请求都拦下来转发给后端"——所以前端用的前缀必须和代理配置里的 key 一致,否则拦不到。
  3. 生产环境被构建替换/dev-api 只存在于开发期。打包时(由 vue.config.jspublicPath.envVUE_APP_BASE_API)会把 /dev-api 换成生产真实前缀(如 /gov/server 或空串同源),线上请求根本不带 /dev-api

结论:/dev-api 是开发期和代理之间的暗号,前后端 + 代理三处统一即可;换成 /api/proxy 都行,只要一致。


三、方案一:浏览器直发(直接用后端地址)

前端代码里直接写后端地址,不经过开发服务器代理。

javascript
// 前端直接请求后端(无代理)
fetch("http://192.168.3.240:81/api/user/info")
  .then(r => r.json())
  .then(console.log);

优势

  • 零前端代理配置,单文件就能跑,最简单。

问题 / 劣势

  1. 开发/测试阶段会被 CORS 阻断:浏览器同源策略拦跨域,本地直接跑不通,除非后端显式开 CORS。
  2. 后端地址写死在前端:换环境(dev/test/prod)得改配置或重新构建,不如代理写相对路径 /dev-api 灵活。
  3. 依赖后端开 CORS 放行:浏览器直发要能通,必须后端显式开启 CORS 头(增加后端配合成本);若后端图省事把 Access-Control-Allow-Origin 设成 *(允许任意源),接口会被任意网站跨域调用,比代理方案(前端不出现后端地址)更不可控。

四、方案二:开发服务器代理(proxy)

前端只写本地相对路径 /dev-api,由 webpack / vite 的开发服务器转发到真实后端,彻底绕过浏览器 CORS。

Vue CLI(vue.config.js)

javascript
module.exports = {
  devServer: {
    host: "0.0.0.0",
    proxy: {
      "/dev-api": {
        target: "http://192.168.3.240:81",   // 后端真实地址
        changeOrigin: true,                   // 改 Host 头,让后端以为请求来自自己
        pathRewrite: { "^/dev-api": "" }       // 去掉前缀再转发
      }
    }
  }
};

Vite(vite.config.js)

javascript
export default {
  server: {
    host: "0.0.0.0",
    proxy: {
      "/server": {
        target: "http://192.168.3.240:81",
        changeOrigin: true,
        ws: true                               // 支持 WebSocket
      }
    }
  }
};

优势

  • 绕过 CORS,前端代码不出现后端真实地址(只写 /dev-api)。
  • 换后端只改 target,环境切换干净。

问题 / 劣势

  • target / pathRewrite 后需重启 dev server:webpack、vite 都在启动时一次性读取配置,没有运行时热更新(见方案三解决)。

五、方案三:免重启方案(动态读取 target,刷新即生效)

针对 Vue CLI(webpack),把后端地址抽到独立文件 .dev-proxy.js,用 http-proxy-middlewarerouter 函数 + 清除 require.cache,每次请求动态读取,改地址只需刷新浏览器,不用重启 dev server

根目录 .dev-proxy.js(日常只改这一行)

javascript
module.exports = {
  target: 'http://192.168.3.72/gov/server'            // dev
  // target: 'http://192.168.4.132:8080'              // 陶璐
  // target: 'http://192.168.3.172:8080'              // 张数数
  // target: 'http://192.168.4.178:8080'              // 张明辉
  // target: 'http://192.168.4.22:8080'               // 柴培炀
}

vue.config.js(一次写好,之后只动上面 .dev-proxy.js)

javascript
"use strict";
const path = require("path");

// 实时读取 .dev-proxy.js 的 target(每次清 require 缓存,改地址无需重启 dev server)
const PROXY_CONF_PATH = path.resolve(__dirname, ".dev-proxy.js");
function getProxyTarget() {
  delete require.cache[require.resolve(PROXY_CONF_PATH)];   // 清缓存,强制重新读取
  const conf = require(PROXY_CONF_PATH);
  if (!conf || !conf.target) throw new Error(".dev-proxy.js 未配置 target");
  return conf.target;
}

const port = process.env.port || process.env.npm_config_port || 90;

module.exports = {
  devServer: {
    host: "0.0.0.0",
    port,
    open: true,
    before(app) {
      app.get("/__proxy-target", (req, res) => res.json({ target: getProxyTarget() }));
      const hpm = require("http-proxy-middleware");
      const createProxy = hpm.createProxyMiddleware || hpm;
      app.use(
        createProxy("/dev-api", {
          target: "http://0.0.0.0",
          changeOrigin: true,
          secure: false,
          pathRewrite: { "^/dev-api": "" },
          router: function () {                 // 每次请求动态返回目标,实现免重启切换
            try { return getProxyTarget(); }
            catch (e) {
              console.log("[proxy] .dev-proxy.js 配置异常,拒绝转发:", e && e.message);
              return "http://127.0.0.1:1";       // 故意不可达,触发 502,绝不偷偷回退
            }
          },
          onProxyRes: function (proxyRes) { proxyRes.headers["x-proxy-target"] = getProxyTarget(); },
          onError: function (err, req, res) {
            let target = "";
            try { target = getProxyTarget(); } catch (e) { target = "(.dev-proxy.js 配置异常:" + (e && e.message) + ")"; }
            console.log("[proxy] 转发失败,当前 target:", target, "|", err && err.message);
            if (res && !res.headersSent) {
              res.writeHead(502, { "Content-Type": "application/json; charset=utf-8" });
              res.end(JSON.stringify({ code: 502, msg: "代理不可达,当前 target:" + target }));
            }
          }
        })
      );
    }
  }
};

优势

  • 日常只改 .dev-proxy.js 一行,刷新浏览器即切换后端,不用重启 dev server
  • 配置异常时拒绝转发并返回 502,不会偷偷连错地址。

问题 / 劣势

  • 只对 Vue CLI(webpack)有效,Vite 没有这种动态 router 机制,改 target 仍需重启。
  • 配置比方案二复杂,新项目若用 Vite 直接用方案二即可。

结论:你们主力是 Ruoyi(Vue CLI),统一用本方案最省事;只有 Vite 新项目才退回方案二(接受重启)。


六、代理参数详解

参数说明示例值
target代理目标地址http://192.168.3.240:81
changeOrigin修改 Host 和 Referertrue
pathRewrite路径重写(去掉前缀){ '^/dev-api': '' }
ws支持 WebSocket 代理true
secure是否检查 SSL 证书false(自签证书时)

七、三种转发方式对比

方式原理适用前端是否见后端地址
浏览器直发前端直接请求后端(需后端开 CORS)极简单页 / 后端已开 CORS
开发代理(proxy)开发服务器转发,绕过浏览器限制开发阶段首选
nginx 反向代理同源部署,生产标准做法生产环境否(同源)

© 2026 开发速查