浅谈跨域
跨域是前端开发中最常见的问题之一,其根源是浏览器的同源策略。下面将从「什么是跨域」「为什么会有跨域」「解决跨域的主流方案」三个维度详细解析,彻底搞懂跨域。
一、什么是跨域?
1. 同源策略(Same-Origin Policy)
跨域的本质是浏览器的同源安全策略:浏览器为了保护用户隐私和安全,禁止不同源的页面之间直接进行数据交互(如 AJAX 请求、DOM 操作、Cookie 读取等)。
同源的定义:两个页面的「协议」「域名」「端口」完全一致,即为同源,只要有一个不同,就是跨域。
2. 跨域的常见场景
举个例子,假设当前页面 URL 是 http://www.example.com:80(默认端口80可省略),以下情况均属于跨域:
| 目标 URL | 跨域原因 | 说明 |
|---|---|---|
https://www.example.com |
协议不同 | http vs https |
http://api.example.com |
域名不同 | 主域名相同,子域名不同 |
http://www.another.com |
域名不同 | 完全不同的域名 |
http://www.example.com:8080 |
端口不同 | 80 vs 8080 |
3. 跨域的影响
同源策略限制了以下行为:
- ❌ 无法发送 AJAX/Fetch 请求到不同源的服务器;
- ❌ 无法读取/操作不同源页面的 DOM(如 iframe 嵌套的页面);
- ❌ 无法读取不同源的 Cookie、LocalStorage、IndexedDB 等存储数据;
- ✅ 但允许资源加载(如
<img>、<script>、<link>、<video>等标签的 src/href 不受同源限制)—— 这是 JSONP 等方案的基础。
二、为什么要有跨域?(同源策略的意义)
同源策略是浏览器的核心安全机制,主要为了防止恶意网站窃取用户数据:
- 假设你登录了银行网站
bank.com,Cookie 中保存了登录状态; - 若没有同源策略,恶意网站
evil.com就能直接通过 AJAX 请求bank.com的接口,窃取你的账户信息、转账等; - 同源策略限制了这种跨域数据交互,保护了用户隐私。
三、解决跨域的主流方案(详细解析)
解决跨域的核心思路有两个:
- 绕过浏览器限制:利用浏览器的“漏洞”或特殊标签(如 JSONP、代理服务器);
- 服务器端配合:服务器明确告诉浏览器“允许跨域”(如 CORS)。
以下是最常用的 6 种方案,按推荐程度排序:
方案 1:CORS(Cross-Origin Resource Sharing,跨域资源共享)—— 最标准、最推荐
CORS 是 W3C 官方推荐的跨域解决方案,需要服务器端配合,通过设置 HTTP 响应头告诉浏览器“允许该域名跨域访问”。
原理
浏览器会自动判断请求是否跨域:
- 对于简单请求(GET/POST/HEAD,无自定义头),浏览器直接发送请求,服务器返回
Access-Control-Allow-Origin等响应头; - 对于非简单请求(如 PUT/DELETE、带自定义头),浏览器会先发送一个 OPTIONS 预检请求,确认服务器允许后再发送真实请求。
服务器端实现(以 Node.js/Express 为例)
1 | |
前端请求(Fetch 示例)
1 | |
优缺点
- ✅ 优点:标准、灵活、支持所有请求方法、可控制权限;
- ❌ 缺点:需要服务器端配合,IE10 以下不支持(但现在基本不用考虑 IE 了)。
方案 2:代理服务器(Nginx / Vue/React DevServer)—— 开发环境最常用
同源策略是浏览器的限制,服务器之间没有跨域问题!因此可以通过「同域代理服务器」转发请求到目标服务器,绕过浏览器限制。
场景 1:开发环境(Vue/React DevServer 代理)
以 Vue CLI 为例,在 vue.config.js 中配置代理:
1 | |
前端请求时直接写 /api/data,DevServer 会自动转发到 http://localhost:3000/data,完美解决跨域。
场景 2:生产环境(Nginx 反向代理)
Nginx 作为前端服务器,将 API 请求转发到后端服务器:
1 | |
此时前端和 Nginx 同域,Nginx 转发请求到后端,无跨域问题。
优缺点
- ✅ 优点:无需修改后端代码、开发/生产环境通用、性能好;
- ❌ 缺点:需要配置代理服务器(Nginx 或 DevServer)。
方案 3:JSONP(JSON with Padding)—— 已逐渐淘汰
JSONP 是一种“古老”的跨域方案,利用 <script> 标签不受同源限制的特点实现。
原理
- 前端动态创建一个
<script>标签,src指向目标接口,并通过callback参数传递回调函数名; - 服务器接收请求后,将数据包裹在回调函数中返回(如
callback({ data: 'xxx' })); - 浏览器加载
<script>后,自动执行回调函数,拿到数据。
实现示例
前端代码:
1 | |
服务器端代码(Node.js):
1 | |
优缺点
- ✅ 优点:兼容性极好(支持老版本 IE)、实现简单;
- ❌ 缺点:仅支持 GET 请求、不安全(容易 XSS 攻击)、需要服务器配合。
- 现状:已逐渐被 CORS 替代,仅在特殊老项目中使用。
方案 4:document.domain —— 仅适用于「主域名相同,子域名不同」的场景
如果两个页面的主域名相同(如 a.example.com 和 b.example.com),可以通过设置 document.domain 为相同的主域名,实现跨域 DOM 操作。
实现示例
页面 A(a.example.com):
1 | |
页面 B(b.example.com):
1 | |
优缺点
- ✅ 优点:实现简单、无需服务器配合;
- ❌ 缺点:仅适用于主域名相同的场景、无法解决 AJAX 跨域、存在安全风险。
方案 5:window.postMessage —— HTML5 官方 API,用于「跨窗口通信」
postMessage 是 HTML5 提供的 API,专门用于不同窗口/iframe 之间的跨域通信(不管是否同源)。
原理
- 发送方:通过
targetWindow.postMessage(data, targetOrigin)发送消息; - 接收方:通过
window.addEventListener('message', (e) => { ... })接收消息。
实现示例
父窗口(http://www.example.com):
1 | |
子窗口(http://www.another.com):
1 | |
优缺点
- ✅ 优点:HTML5 标准、安全(可验证 origin)、支持任意窗口通信;
- ❌ 缺点:仅适用于窗口/iframe 之间的通信,无法解决 AJAX 跨域。
方案 6:WebSocket —— 不受同源策略限制
WebSocket 是 HTML5 提供的全双工通信协议,本身不受同源策略限制,因此可以直接跨域通信。
简单示例
1 | |
优缺点
- ✅ 优点:不受同源限制、全双工通信、性能好;
- ❌ 缺点:需要服务器支持 WebSocket、仅适用于实时通信场景。
四、方案总结与推荐
| 方案 | 推荐指数 | 适用场景 |
|---|---|---|
| CORS | ⭐⭐⭐⭐⭐ | 绝大多数生产环境场景 |
| 代理服务器(Nginx/DevServer) | ⭐⭐⭐⭐⭐ | 开发环境、生产环境通用 |
| postMessage | ⭐⭐⭐⭐ | 跨窗口/iframe 通信 |
| document.domain | ⭐⭐⭐ | 主域名相同的子域名跨域 |
| JSONP | ⭐⭐ | 老项目兼容(已淘汰) |
| WebSocket | ⭐⭐⭐⭐ | 实时通信场景 |
最终建议:
- 开发环境:用 DevServer 代理(Vue/React 都支持);
- 生产环境:优先用 CORS,若无法修改后端代码则用 Nginx 反向代理;
- 跨窗口通信:用 postMessage。