什么是跨域
浏览器执行同源策略, 即协议、域名、端口号三者必须全部一致,限制的行为如cookie、localStorage、Dom元素、ajax请求等
跨域的常见解决办法
jsonp
- jsonp的实现原理
它是利用script标签的src属性来实现跨域.它将前端方法作为参数传递到服务器端,然后由服务器端注入参数之后再返回,实现服务器端向客户端通信。 - 简单实现
// 创建一个script标签,将传递的参数拼接赋值给script的src
function jsonp({ url, params, cb }) {
return new Promise((resolve, reject) => {
// 声明全局函数
window[cb] = function (data) {
resolve(data);
document.body.removeChild(script);
};
params = {...params, cb};
let arrs = [];
for (let key in params) { // 将参数拼接成wd=love&cb=show
arrs.push(`${key}=${params[key]}`);
}
let script = document.createElement('script');
script.src = `${url}?${arrs.join('&')}`;
document.body.appendChild(script);
})
}
jsonp({
url: 'http://localhost:3000/say',
params: {
wd: 'love',
},
cb: 'show',
}).then(data => {
console.log(data);
});
缺点: 只能发送get请求
跨域资源共享CORS
CORS允许浏览器向跨源服务器发送ajax请求,它需要浏览器和服务器同时支持。当浏览器发现AJAX请求跨源,会自动添加一些附加的头信息,服务器实现对应的CORS接口,有时还会多出一次附加的请求,然后可以跨源通信。
- 相关CORS
Access-Control-Allow-Origin // 允许访问的源
Access-Control-Allow-Headers // 允许请求头
Access-Control-Allow-Credentials // 允许携带cookie, 此时,客户端也需要在在AJAX请求中打开withCredentials属性 CORS请求默认不发送Cookie和HTTP认证信息
Access-Control-Expose-Headers // 允许前端获取头 预检请求时使用
Access-Control-Allow-Methods // 允许方法 预检请求时使用
Access-Control-Max-Age // 预检间隔时间
...
postMessage
H5提供的跨域请求方法.
// 发送信息
<iframe src="http://localhost:3009/b.html" frameborder="0" id='frame' onload="load()"></iframe>
<script>
// 页面间通信
function load () {
let frame = document.getElementById('frame');
// 拿到b窗口的window 向其发送信息
frame.contentWindow.postMessage('我是a', 'http://localhost:3009');
window.onmessage = (e) => {
console.log(e.data);
}
}
</script>
// 接收信息
<script>
window.onmessage = (e) => {
console.log(e.data);
e.source.postMessage('我是b', e.origin);
}
</script>
window.name
window.name属性可以在不同的域下保存,通过子窗口之间通信实现数据传输.跨域实现如下:
a和b是同域--> http://localhost:3000
c--> http://localhost:3009
目标: a获取c的数据
实现过程: 在a.html中通过iframe引用c,c把值放在window.name,a把引用的地址改到b,通过b拿到c数据
// a.html
<iframe src="http://localhost:3009/c.html" frameborder="0" id='frame' onload="load()"></iframe>
<script>
let first = true;
function load() {
if (first) {
let iframe = document.getElementById('iframe');
iframe.src="http://localhost:3000/b.html";
first = false;
} else {
console.log(iframe.contentWindow.name);
}
}
</script>
// c.html
<script>
window.name = '我是c';
</script>
页面中的iframe支持跨域,iframe-b可以拿到iframe-c中的数据.a和iframe-b同源,所以a可以通过b拿到c的数据
location.hash
a和b是同域--> http://localhost:3000
c--> http://localhost:3009
目标: a获取c的数据
实现过程: a给c传递hash值 c将此hash传递给b b将结果放到a的hash中
// a.html
<iframe src="http://localhost:3009/c.html#iama" frameborder="0" id='frame'></iframe>
<script>
window.onhashchange = () => {
console.log(location.hash, 'c传递的hash');
}
</script>
// b.html
<script>
window.parent.parent.location.hash = location.hash;
</script>
// c.html
<script>
console.log(location.hash, 'a传递的hash');
let iframe = document.createElement('iframe');
iframe.src = 'http://localhost:3000/b.html#iamc';
document.body.appendChild(iframe);
</script>
websocket
此处使用第三方插件websocket.io
// 客户端
let socket = new WebSocket('ws://localhost:3000');
socket.onopen = () => { // 连接成功
socket.send('我是a');
}
socket.onmessage = (e) => { // 监听服务端消息
console.log(e.data);
}
// 服务端
let express = require('express');
let app = express();
let WebSocket = require('ws');
let wss = new WebSocket.Server({port: 3000});
wss.on('connection', (ws) => { // 连接成功
ws.on('message', (data) => {
console.log(data);
})
ws.send('我是服务');
})
nginx
location / { #访问文件路径
try_files $uri /index.html;
}
location ~* .*(html|json)$ {
expires -1s;
add_header 'Cache-Control' 'no-store';
add_header 'Access-Control-Allow-Origin' '*'; # 添加跨域头
}
domain
一般针对二级域名,结合iframe实现跨域