八种跨域

跨域是指在同一网站下,由于浏览器的安全机制,不能直接访问其他网站的数据。而前端开发中,由于业务的需要,经常需要和其他网站进行数据交互。因此,解决跨域问题成为了前端开发必备的技能。

本文将详细介绍跨域的八种实现方式,以及它们的使用场景、优缺点。

JSONP

JSONP 是一种利用 <script> 标签进行跨域请求的方式。它的原理是在请求地址中添加一个回调函数名,服务端在返回数据时会将数据作为参数传入该回调函数中,并返回给客户端。客户端则可以通过定义这个回调函数来处理返回的数据。

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function jsonp(url, callback) {
const script = document.createElement("script");
script.src = `${url}&callback=${callback}`;
document.body.appendChild(script);
window[callback] = function (data) {
document.body.removeChild(script);
delete window[callback];
callback(data);
};
}

jsonp("http://example.com/data", function (data) {
console.log(data);
});

使用场景:

  • 只支持 GET 请求。
  • 能够支持跨域请求,但只能处理 JSON 数据。
  • 适用于数据量较小、不需要特别保密的场景。

优缺点:

  • 优点:实现简单、支持跨域请求。
  • 缺点:只能处理 JSON 数据、无法处理错误信息、存在安全风险。

CORS

CORS 是一种官方推荐的跨域解决方案。它的原理是在发送请求时,服务端在响应头中添加一个 Access-Control-Allow-Origin 字段,标识允许哪个源进行访问。客户端则可以通过设置 withCredentials 属性来携带认证信息,以及在响应头中获取对应的字段。

示例代码:

1
2
3
4
5
6
7
8
9
const xhr = new XMLHttpRequest();
xhr.open("GET", "http://example.com/data");
xhr.withCredentials = true;
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
console.log(xhr.responseText);
}
};
xhr.send();

使用场景:

  • 支持 GET、POST 等类型的请求。
  • 能够支持跨域请求,同时支持多种数据类型。
  • 适用于大部分的场景。

优缺点:

  • 优点:能够支持跨域请求、支持多种数据类型。
  • 缺点:需要服务端配合设置响应头、性能稍低。

WebSocket

WebSocket 是一种双向通信协议,它可以在浏览器和服务端之间建立一个持久化的连接,以实现实时通信。由于它是基于 HTTP 协议的,因此支持跨域请求。

示例代码:

1
2
3
4
const ws = new WebSocket("ws://example.com/socket");
ws.onmessage = function (event) {
console.log(event.data);
};

使用场景:

  • 可以实现双向通信、实时更新。
  • 适用于聊天室、游戏等实时性较高的场景。

优缺点:

  • 优点:能够实现双向通信、实时更新。
  • 缺点:需要服务端配合实现、只能处理文本数据、不稳定。

postMessage

postMessage 是 HTML5 中新增的一种跨窗口通信机制,它可以在不同窗口、甚至不同域名之间进行数据传递。它的原理是通过调用 window.postMessage() 方法,在目标窗口中触发一个 message 事件,从而实现数据的传递。

示例代码:

1
2
3
4
5
6
7
8
9
// 发送消息
window.parent.postMessage("hello", "http://example.com");

// 接收消息
window.addEventListener("message", function (event) {
if (event.origin === "http://example.com") {
console.log(event.data);
}
});

使用场景:

  • 可以进行不同窗口、不同域名之间的数据传递。
  • 适用于嵌套页面、多窗口通信等场景。

优缺点:

  • 优点:可以进行不同窗口、不同域名之间的数据传递。
  • 缺点:需要进行安全性检查、可能存在 XSS 攻击风险。

nginx 反向代理

通过配置 nginx 的反向代理,可以实现对目标地址的访问,并将响应返回给客户端。由于是在服务端进行请求,因此不会受到浏览器的同源策略限制。

示例代码:

1
2
3
location /api/ {
proxy_pass http://example.com/data/;
}

使用场景:

  • 用于解决前端直接访问跨域 API 的问题。
  • 适用于请求量较大、需要保密的场景。

优缺点:

  • 优点:能够支持跨域请求、保护了 API 的安全性。
  • 缺点:需要配置服务器,增加了服务器端的压力。

HTML5 XHR2

HTML5 中新增的 XHR2 对象,支持跨域请求,并且可以处理二进制数据和进度信息。

示例代码:

1
2
3
4
5
6
7
8
9
10
11
const xhr = new XMLHttpRequest();
xhr.open("GET", "http://example.com/data");
xhr.responseType = "blob";
xhr.onload = function () {
const reader = new FileReader();
reader.onloadend = function () {
console.log(reader.result);
};
reader.readAsText(xhr.response);
};
xhr.send();

使用场景:

  • 支持跨域请求,能够处理二进制数据和进度信息。
  • 适用于需要上传、下载文件等场景。

优缺点:

  • 优点:支持跨域请求、能够处理二进制数据和进度信息。
  • 缺点:浏览器兼容性不一、需要服务端配合设置响应头。

document.domain

通过设置 document.domain 的值,可以实现不同子域名之间的跨域通信。但是该方式只适用于主域名相同、子域名不同的情况下。

示例代码:

1
2
3
4
5
// 在 A 页面中设置
document.domain = "example.com";

// 在 B 页面中设置
document.domain = "example.com";

使用场景:

  • 只适用于主域名相同、子域名不同的情况下。
  • 适用于不同子域名之间进行数据传递的场景。

优缺点:

  • 优点:实现简单、不需要浏览器支持。
  • 缺点:只适用于主域名相同、子域名不同的情况下。

iframe

通过在页面中插入一个 iframe,可以在其中加载目标页面,从而实现跨域通信。但是该方式存在一些安全问题,容易遭受 CSRF 攻击。

示例代码:

1
<iframe src="http://example.com"></iframe>

使用场景:

  • 适用于嵌套页面、跨域数据传输等场景。

优缺点:

  • 优点:能够实现跨域通信。