前端跨域全解:原理+9种方案+实战代码,一篇吃透
跨域是前端面试高频考点,也是开发必踩坑。本文从同源策略讲起,拆解跨域本质,详解9种主流跨域方案,附可直接复制的实战代码,帮你一次吃透跨域。
一、先搞懂:什么是同源策略?
跨域的根源,是浏览器的同源策略(Same-Origin Policy)——这是浏览器最核心的安全机制,用于隔离不同源的资源,防止恶意网站窃取数据。
同源判定3要素(必须完全一致)
- 协议:http/https
- 域名:主域名/子域名(www.xxx.com ≠ api.xxx.com)
- 端口:8080≠3000
举个例子
前端:http://localhost:5173
后端:http://localhost:3000 → 端口不同,跨域
前端:https://www.juejin.cn
前端:http://www.juejin.cn → 协议不同,跨域
二、跨域从哪来?浏览器的安全红线
- 跨域只存在于浏览器,服务器之间请求无跨域限制
- 浏览器会拦截不同源的 Ajax/Fetch 请求,但允许
<script>/<img>/<link>等标签跨域加载资源
三、同源策略到底限制了什么?
- 无法读取不同源的 Cookie、LocalStorage、IndexedDB
- 无法获取不同源的 DOM 元素
- 无法发送不同源的 Ajax/Fetch 请求(核心痛点)
四、9种跨域解决方案,原理+代码全解析
1. JSONP:最古老的跨域方案(兼容IE6+)
原理:利用 <script> 标签无跨域限制,通过回调函数获取数据
优点:兼容性拉满;缺点:仅支持GET,不安全,不支持复杂请求
// 前端代码
function handleData(res) {
console.log("JSONP返回数据:", res);
}
const script = document.createElement("script");
script.src = "http://localhost:3000/api/jsonp?callback=handleData";
document.body.appendChild(script);
// 后端Node.js返回
app.get("/api/jsonp", (req, res) => {
const callback = req.query.callback;
const data = { name: "掘金", msg: "JSONP跨域成功" };
res.send(`${callback}(${JSON.stringify(data)})`);
});
2. CORS:现代前端首选标准方案
全称:Cross-Origin Resource Sharing(跨域资源共享) 原理:后端设置响应头,明确允许指定源访问,浏览器验证通过后放行 分类:简单请求、预检请求(OPTIONS)
优点:支持所有请求方式,安全规范;缺点:依赖后端配置
// 后端Node.js + Express 配置CORS
const express = require("express");
const app = express();
// 允许所有源(开发用)
app.use((req, res, next) => {
res.setHeader("Access-Control-Allow-Origin", "*");
res.setHeader("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE");
res.setHeader("Access-Control-Allow-Headers", "Content-Type");
next();
});
// 带Cookie的CORS
// res.setHeader("Access-Control-Allow-Credentials", "true");
// res.setHeader("Access-Control-Allow-Origin", "http://localhost:5173");
3. 前端本地代理:开发环境神器(Webpack/Vite)
原理:本地启动代理服务器,转发请求绕过浏览器跨域限制 优点:前端无感知,开发效率高;缺点:仅适用于开发环境
// Vite 代理配置 vite.config.js
export default {
server: {
proxy: {
"/api": {
target: "http://localhost:3000", // 后端地址
changeOrigin: true, // 修改Origin为目标源
rewrite: (path) => path.replace(/^\/api/, ""),
},
},
},
};
// Webpack 代理配置 webpack.config.js
devServer: {
proxy: {
"/api": {
target: "http://localhost:3000",
changeOrigin: true,
},
},
}
4. Nginx反向代理:生产环境首选
原理:Nginx作为网关,转发前端请求到后端,浏览器只认Nginx为同源 优点:高性能,无侵入,适合生产;缺点:需运维配置
# Nginx 配置
server {
listen 80;
server_name localhost;
location /api/ {
proxy_pass http://localhost:3000/; # 转发到后端
add_header Access-Control-Allow-Origin *;
}
}
5. postMessage:iframe/跨窗口通信神器
原理:HTML5标准API,实现窗口/iframe之间安全通信 适用场景:iframe嵌套、多窗口数据传递
// 父页面发送消息
const iframe = document.getElementById("child-iframe");
iframe.contentWindow.postMessage("来自父页面的消息", "http://localhost:3000");
// 子页面接收消息
window.addEventListener("message", (e) => {
if (e.origin !== "http://localhost:5173") return; // 验证源安全
console.log("子页面收到:", e.data);
});
6. WebSocket:全双工无跨域
原理:WebSocket协议不受同源策略限制,实现实时通信 适用场景:聊天室、直播、实时数据推送
// 前端WebSocket
const ws = new WebSocket("ws://localhost:3000/ws");
ws.onopen = () => ws.send("前端连接成功");
ws.onmessage = (e) => console.log("收到服务端消息:", e.data);
7. document.domain:主域相同的子域跨域
原理:强制设置相同主域名,实现子域间通信 适用场景:a.xxx.com 与 b.xxx.com 跨域
// 两个页面都设置
document.domain = "xxx.com";
8. window.name:轻量iframe跨域
原理:iframe的name属性跨域不重置,用于临时存储数据 优点:简单;缺点:仅iframe场景,兼容性一般
9. location.hash:哈希传值跨域
原理:通过URL哈希值传递数据,监听hashchange事件 优点:无依赖;缺点:数据量小,不安全,已淘汰
五、方案对比:怎么选才对?
| 方案 | 支持请求 | 兼容性 | 生产推荐 | 适用场景 |
|---|---|---|---|---|
| CORS | 全部 | 现代浏览器 | ✅✅✅ | 绝大多数前后端分离项目 |
| 本地代理 | 全部 | 全兼容 | ❌(仅开发) | 本地开发调试 |
| Nginx代理 | 全部 | 全兼容 | ✅✅✅ | 生产环境部署 |
| JSONP | GET | IE6+ | ❌ | 老旧浏览器兼容 |
| postMessage | 通信 | 现代浏览器 | ✅ | iframe/跨窗口 |
| WebSocket | 全双工 | 现代浏览器 | ✅ | 实时通信 |
六、实战最佳实践
- 开发环境:用Webpack/Vite本地代理,零配置快速开发
- 生产环境:后端CORS + Nginx反向代理,安全高效
- 特殊场景:iframe用postMessage,实时通信用WebSocket
- 安全提醒:CORS不要用
*,指定具体域名;带Cookie必须配置Access-Control-Allow-Credentials
七、总结
跨域的核心是浏览器同源策略,解决思路只有两种:
- 让浏览器认为请求是同源的(代理、Nginx)
- 让后端明确授权允许跨域(CORS)
现代前端开发,CORS + 代理 + Nginx 已覆盖99%场景,JSONP等历史方案仅做兼容了解即可。
本文已覆盖面试与开发全场景,建议收藏备用~ 有问题欢迎评论区交流!
标签:#前端 #跨域 #CORS #同源策略 #面试
文章已按掘金风格完成,包含原理、代码、选型、最佳实践,可直接发布。需要我帮你精简成面试版、补充Vue/React实战示例或调整排版吗?