js跨域的解决办法
2020-07-23 本文已影响0人
zhulichao
浏览器都有一个同源策略,其限制之一是不能通过ajax的方法去请求不同源中的文档;第二是限制浏览器中不同域的框架之间是不能进行js的交互操作的。但是不同的框架之间(父子或同辈),是能够获取到彼此的window对象的,但却不能使用获取到的window对象的属性和方法,**postMessage方法除外,**可以当做是只能获取到一个几乎无用的window对象。只要协议、域名、端口有任何一个不同,都被当作是不同的域。要解决跨域的问题,我们可以使用以下几种方法:
**方式一、通过jsonp跨域**
jsonp是利用`<script>`标签可以引入不同域上的js脚本文件来实现的,通过script标签引入一个js文件,这个js文件载入成功后会执行我们在url参数中指定的函数,并且会把我们需要的json数据作为参数传入。所以jsonp是需要服务器端的页面进行相应的配合的。
```
/*
js原生写法,通过http://example.com/data.php?callback=dosomething得到的js文件,就是我们之前定义的dosomething函数,并且它的参数就是我们需要的json数据,这样就跨域获得了我们需要的数据。
*/
<script>
function dosomething(jsondata) {
// 相应操作
}
</script>
<script src="http://example.com/data.php?callback=dosomething"></script>
/*
jQuery写法,jQuery会自动生成一个全局函数来替换callback=?中的问号,之后获取到数据后又会自动销毁。$.getJSON方法会自动判断是否跨域,不跨域就调用普通的ajax方法,跨域会以异步加载js文件的形式来调用jsonp的回调函数。
*/
<script>
$.getJSON('http://example.com/data.php?callback=?', function(jsondata){
// 相应操作
});
</script>
```
<br />
**方式二、通过修改document.domain来跨子域**
针对不同域的框架这种情况,是无法通过在页面中书写js代码来获取iframe中的东西的,这个时候可以使用document.domain,把这两个页面的document.domain都设成相同的域名就可以了。但要注意,只能把document.domain设置成自身或更高一级的父域,且主域必须相同,如果当前文档的domain就是要设置的域名,还是必须显示的设置
document.domain。这样我们就可以通过js访问到iframe中的各种属性和对象了。
`http://www.example.com/a.html`中:
```
<iframe src="http://example.com/b.html" id="iframe" onLoad="test()"></iframe>
<script>
document.domain = 'example.com'; // 设置成主域
function test() {
alert(document.getElementById('iframe').contentWindow);
}
</script>
```
`http://example.com/b.html`中:
```
<script>
// 在iframe载入的这个页面也设置document.domain,使之与主页面的document.domain相同
</script>
```
但是如果想在a.html页面中通过ajax直接请求b.html页面,即使设置了相同的document.domain也还是不行的,修改document.domain的方法只适用于不同子域的框架间的交互。如果想通过ajax的方法去与不同子域的页面交互,除了使用jsonp的方法外,还可以用一个隐藏的iframe来做一个代理。原理就是让这个iframe载入一个与你想要通过ajax获取数据的目标页面处在相同域的页面,这个iframe中的页面是可以正常使用ajax去获取你要的数据的,然后通过修改document.domain的方法,通过js完全控制这个iframe,这样就可以让iframe去发送ajax请求,然后收到的数据我们也可以获取到。
**方式三、使用window.name来进行跨域**
window.name属性有个特征,即在一个窗口(window)的生命周期内,窗口载入的所有的页面都是共享一个window.name的,即使这些页面是不同域的或url发生变化,每个页面对window.name都有读写的权限,window.name是持久存在一个窗口载入过的所有页面中的,不会因新页面的载入而进行重置。window.name的值只能是字符串的形式,最大能允许2M左右甚至更大的一个容量,具体取决于不同的浏览器,但一般是够用了。例如`www.example.com/a.html`页面里的js需要获取另一个位于不同域的页面`www.cnblogs.com/data.html`里的数据,示例代码如下:
`www.cnblogs.com/data.html`中:
```
<script>
window.name = '我就是页面a.html想要的数据,所有可以转化成字符串来传递的数据都可以在这里使用!!!';
</script>
```
`www.example.com/a.html`中:
```
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title>window.name跨域</title>
<script>
function getData() {
var iframe = document.getElementById('proxy');
// 这个时候a.html与iframe已经是同源了,可以互相访问
iframe.onload = function() {
// 获取iframe里的window.name,也就是data.html页面给它设置的数据
var data = iframe.contentWindow.name;
alert(data);
};
// 这里的b.html为随便的一个页面,只要与a.html同源的就行
iframe.src = 'b.html';
}
</script>
</head>
<body>
<iframe id="proxy" src="http://www.cnblogs.com/data.html" style="display:none" onload="getData()" />
</body>
</html>
```
**方式四、使用HTML5中新引进的window.postMessage方法来跨域传送数据**
`window.postMessage(message, targetOrigin)`方法是html5新引进的特性,不支持IE6、7旧浏览器,可以向其它的window对象发送消息,无论这个window对象是属于同源或不同源,该方法的第一个参数message为要发送的消息,类型只能为字符串;第二个参数targetOrigin用来限定接收消息的那个window对象所在的域,如果不想限定域,可以使用通配符 * 。接收消息的window对象,通过监听自身的message事件来获取传过来的消息,消息内容储存在该事件对象的data属性中。
`test.com/a.html`中:
```
<script>
function onLoad() {
var iframe = document.getElementById("iframe");
var win = iframe.contentWindow;
// 向不同域发送消息
win.postMessage('我是来自页面a的消息!', '*');
}
</script>
<iframe id="iframe" src="http://www.test.com/b.html" onload="onLoad()" />
```
`www.test.com/b.html`中:
```
<script>
window.onmessage = function(e) {
e = e || event;
alert(e.data);
}
</script>
```