在前端开发中,跨域问题就像一堵无形的墙,阻碍着本地开发环境与后端API的正常通信。当我们在React脚手架创建的项目中开发时,前端运行在localhost:3000,而后端服务可能运行在localhost:5000或其他端口,浏览器出于安全考虑会阻止这种跨域请求。这就好比两个说不同语言的人需要翻译才能沟通,代理服务器就是这个翻译官。
代理配置的核心价值在于:
这是React官方推荐的最简代理配置方式,只需在项目根目录的package.json中添加一行配置:
json复制{
"proxy": "http://localhost:5000"
}
这个配置的工作原理就像邮局的信件转发服务:
http://localhost:3000/api/data请求/api/datahttp://localhost:5000/api/data重要提示:这种配置方式要求使用React Scripts 2.0+版本,它是create-react-app内置的功能,无需额外安装依赖。
这种配置最适合以下情况:
但存在三个明显局限:
对于需要更复杂代理规则的场景,我们需要使用http-proxy-middleware这个专业工具。首先确保项目已经安装了该依赖:
bash复制npm install http-proxy-middleware --save-dev
# 或
yarn add http-proxy-middleware -D
然后在src目录下创建setupProxy.js文件(位置必须准确)。这个文件会被React开发服务器自动加载,不需要在任何地方显式导入。
下面是一个功能完备的多代理配置示例:
javascript复制const { createProxyMiddleware } = require('http-proxy-middleware')
module.exports = function(app) {
app.use(
'/runner', // 代理路径前缀
createProxyMiddleware({
target: 'http://localhost:5000',
changeOrigin: true,
pathRewrite: {
'^/runner': '/prefix' // 将/runner替换为/prefix
},
onProxyReq: (proxyReq, req, res) => {
// 可以在这里添加自定义请求头
proxyReq.setHeader('X-Special-Header', 'value')
}
})
)
app.use(
'/graphql',
createProxyMiddleware({
target: 'http://api.example.com',
ws: true, // 代理WebSocket连接
logLevel: 'debug' // 显示详细日志
})
)
}
路径匹配规则:
'/runner':匹配所有以/runner开头的请求['/api', '/auth']:可以传入数组匹配多个路径changeOrigin的作用:
true:将Host头改为目标地址的host(推荐)false:保持原始Host头(可能被服务器拒绝)路径重写魔法:
javascript复制pathRewrite: {
'^/old-path': '/new-path', // 完全替换
'^/remove': '' // 删除前缀
}
这个功能特别有用,比如:
在实际项目中,我们通常需要区分开发、测试和生产环境:
javascript复制const TARGET = process.env.REACT_APP_ENV === 'production'
? 'https://api.example.com'
: 'http://localhost:5000'
module.exports = function(app) {
app.use(
'/api',
createProxyMiddleware({
target: TARGET,
changeOrigin: true
})
)
}
配合.env文件管理环境变量:
code复制# .env.development
REACT_APP_ENV=development
# .env.production
REACT_APP_ENV=production
对于需要实时通信的场景,WebSocket的代理需要特殊处理:
javascript复制app.use(
'/socket.io',
createProxyMiddleware({
target: 'ws://localhost:6000',
ws: true, // 关键配置
changeOrigin: true
})
)
当后端使用HTTPS特别是自签名证书时:
javascript复制app.use(
'/secure',
createProxyMiddleware({
target: 'https://localhost:8443',
secure: false, // 忽略证书验证
changeOrigin: true
})
)
假设后端API路径与前端期望的不一致:
javascript复制pathRewrite: function(path, req) {
// /api/users => /v1/users
if(path.includes('/api')) {
return path.replace('/api', '/v1')
}
// /auth/login => /oauth2/token
if(path.includes('/auth')) {
return '/oauth2/token'
}
return path
}
代理性能调优:
javascript复制app.use(
'/heavy',
createProxyMiddleware({
target: 'http://backend:8000',
proxyTimeout: 30000, // 超时设置
xfwd: true, // 添加x-forwarded-*头
cookieDomainRewrite: {
"*": "localhost" // 重写cookie域
}
})
)
日志与调试技巧:
logLevel: 'debug'查看详细代理日志onError回调处理代理错误:javascript复制onError: (err, req, res) => {
res.writeHead(500, {
'Content-Type': 'text/plain'
})
res.end('代理请求失败: ' + err.message)
}
安全注意事项:
随着Vite的流行,其代理配置也值得了解(vite.config.js):
javascript复制export default defineConfig({
server: {
proxy: {
'/api': {
target: 'http://localhost:5000',
changeOrigin: true,
rewrite: path => path.replace(/^\/api/, '')
}
}
}
})
与webpack代理的主要区别:
在大型项目中,代理配置应该遵循这些原则:
一个推荐的项目结构:
code复制/src
/setupProxy
base.js # 基础配置
auth.js # 认证相关
api-v1.js # APIv1路由
api-v2.js # APIv2路由
setupProxy.js # 主入口
在实际项目中,我通常会创建一个代理配置工厂函数来统一管理规则:
javascript复制// src/setupProxy/proxyFactory.js
const createProxyRule = (prefix, target, options = {}) => ({
[prefix]: {
target,
changeOrigin: true,
...options
}
})
module.exports = { createProxyRule }
// 在setupProxy.js中使用
const { createProxyRule } = require('./setupProxy/proxyFactory')
module.exports = function(app) {
Object.entries({
...createProxyRule('/api', 'http://localhost:5000'),
...createProxyRule('/auth', 'http://auth:8000', {
pathRewrite: { '^/auth': '' }
})
}).forEach(([path, config]) => {
app.use(path, createProxyMiddleware(config))
})
}