JavaScript跨域解决方案详解:原理、实现与兼容性分析

作者:代码拾荒时间:2026-10-29 13:17:06浏览:86

本文详细解析JavaScript跨域访问的常见场景与解决方案。涵盖基于iframe的document.domain设置、利用script标签的JSONP机制、通过window.name持久化特性实现的跨域通信,以及HTML5引入的postMessage API。文章对比了各方法的原理、代码实现、适用场景及浏览器兼容性,帮助开发者根据项目需求选择最合适的跨域策略。

JavaScript跨域概述

JavaScript跨域访问是Web开发中常见的挑战。所谓跨域,是指在一个域中加载的脚本尝试获取或操作另一个域中文档的属性。本文将介绍三种主要的JavaScript跨域实现方式。

基于iframe的跨域方案

第一种方法基于iframe实现跨域。这种方式要求两个域的域名具有相同的二级域名特征,例如aa.xx.com和bb.xx.com,即它们必须属于同一个基础域(如都是xxx.com或xxx.com.cn),使用相同的协议(如http)以及相同的端口(如80)。通过在两个页面中同时设置document.domain,可以实现父页面调用子页面函数的功能。


<html> 
<head> 
 <script> 
 document.domain = "xx.com"; 
 function aa(){ 
  alert("p"); 
 } 
 </script> 
</head> 
<body> 
 <iframe src="http://localhost:8080/CmsUI/2.html" id="i"> 
 </iframe> 
 <script> 
 document.getElementById('i').onload = function(){ 
  var d = document.getElementById('i').contentWindow; 
  d.a(); 
 }; 
 </script> 
 </body> 
</html> 

页面一:

页面二:


<html> 
 <head> 
 <script> 
 document.domain = "xx.com"; 
 function a(){ 
 alert("c"); 
  } 
 </script> 
 </head> 
 <body> 
 </body> 
</html> 

配置完成后,父页面即可调用子页面定义的函数,从而实现基于iframe的JavaScript跨域访问。

基于script标签的跨域方案

第二种方法基于script标签实现跨域。

script标签本身具备加载其他域资源的能力,不受浏览器同源策略的限制。可以通过在页面中动态创建script标签来加载其他域的脚本,代码如下:

Java代码


var script = document.createElement('script'); 
script.src = "http://aa.xx.com/js/*.js"; 
document.body.appendChild(script); 

通过动态创建script标签,可以加载其他域的JS文件,进而在本页面调用加载后的JS函数。该方法的局限性在于只能加载JS文件,无法直接加载其他域的文档数据。JSONP正是基于此原理实现跨域数据传输。JSONP通过向其他域请求时传入一个callback参数,其他域后台将该参数值与JSON数据封装成JavaScript函数返回。由于请求是通过script标签发起的,浏览器会将返回的字符串解析为JavaScript代码执行,从而完成域间数据传递。jQuery对JSONP的支持也基于此方案。

后台代理跨域方案

第三种方法是后台代理方式。

该方法可以解决所有类型的跨域问题。其核心思路是将后台服务器作为代理,前端对其它域的请求转交给本域后台,本域后台模拟HTTP请求访问其他域,再将结果返回给前端。这种方式的优势在于无论请求的是文档还是JS文件,均可实现跨域访问。

下表展示了相对于http://store.company.com/dir/page.html的同源检测结果:

JavaScript跨域概述 

要解决跨域问题,可以使用以下几种具体方法:

JSONP跨域详解

一、通过JSONP跨域

在JavaScript中,直接使用XMLHttpRequest请求不同域的数据是不被允许的。然而,在页面中引入不同域的JS脚本文件却是可行的,JSONP正是利用这一特性来实现跨域数据获取。

例如,假设a.html页面需要通过Ajax获取不同域上的JSON数据,目标地址为http://example.com/data.php,那么a.html中的代码可以如下编写:

 JavaScript跨域概述

可以看到,获取数据的地址后附加了一个callback参数,通常使用此名称,但使用其他名称亦可。如果目标JSONP地址页面不由你控制,则需遵循提供数据方的格式规范。

由于该请求被视为引入JS文件,因此http://example.com/data.php返回的必须是可执行的JS代码。该页面的PHP代码可能如下所示:

 JavaScript跨域概述

最终该页面输出的结果为:

 JavaScript跨域概述

因此,通过http://example.com/data.php?callback=dosomething获取的JS文件,即是我们之前定义的dosomething函数,其参数即为所需的JSON数据。这样我们就成功跨域获取了所需数据。

JSONP的原理清晰:通过script标签引入JS文件,文件加载成功后会执行URL参数中指定的函数,并将JSON数据作为参数传入。因此,JSONP需要服务器端页面的配合。

了解JSONP原理后,我们可以使用JavaScript动态生成script标签来进行跨域操作,无需手动编写script标签。如果使用jQuery,可以通过其封装的方法方便地进行JSONP操作。

 JavaScript跨域概述

原理相同,但无需手动插入script标签或定义回调函数。jQuery会自动生成一个全局函数替换callback=?中的问号,获取数据后自动销毁,实际上起到了临时代理函数的作用。$.getJSON方法会自动判断是否跨域:若不跨域,则调用普通Ajax方法;若跨域,则以异步加载JS文件的形式调用JSONP回调函数。

修改document.domain跨子域

二、通过修改document.domain来跨子域

浏览器具有同源策略,其限制之一是无法通过Ajax请求不同源的文档。另一限制是不同域的框架间不能进行JavaScript交互。需要注意的是,不同框架之间(父子或同辈)可以获取彼此的window对象,但通常无法使用获取到的window对象的属性和方法(HTML5中的postMessage方法是一个例外,部分浏览器如IE6也可使用top、parent等少数属性)。总之,可视为只能获取到一个几乎无用的window对象。例如,页面地址为http://www.example.com/a.html,其中包含一个src为http://example.com/b.html的iframe。显然,该页面与iframe框架不同域,因此无法通过页面中的JavaScript代码获取iframe中的内容:

 JavaScript跨域概述

此时,document.domain即可发挥作用。只需将http://www.example.com/a.html和http://example.com/b.html这两个页面的document.domain设置为相同的域名即可。但需注意,document.domain的设置有限制:只能将其设置为自身或更高一级的父域,且主域必须相同。例如,a.b.example.com中的文档可将document.domain设置为a.b.example.com、b.example.com或example.com中的任意一个,但不能设置为c.a.b.example.com(因为这是当前域的子域),也不能设置为baidu.com(因为主域不同)。

在页面http://www.example.com/a.html中设置document.domain:

 JavaScript跨域概述

在页面http://example.com/b.html中也设置document.domain,这也是必须的,即使该文档的domain已经是example.com,仍需显式设置document.domain的值:

 JavaScript跨域概述

这样,我们就可以通过JavaScript访问iframe中的各种属性和对象了。

不过,如果想在http://www.example.com/a.html页面中通过Ajax直接请求http://example.com/b.html页面,即使设置了相同的document.domain也不行。因此,修改document.domain的方法仅适用于不同子域框架间的交互。若需通过Ajax与不同子域页面交互,除使用JSONP外,还可使用隐藏的iframe作为代理。原理是让iframe加载一个与目标Ajax数据处于同域的页面,该iframe中的页面可正常使用Ajax获取数据,再通过修改document.domain的方法,使JavaScript完全控制该iframe,从而让iframe发送Ajax请求,并获取返回数据。

window.name跨域机制

三、使用window.name进行跨域

window对象有一个name属性,其特点是:在一个窗口(window)的生命周期内,窗口加载的所有页面都共享一个window.name,每个页面都对window.name有读写权限。window.name会持久存在于窗口加载过的所有页面中,不会因新页面的载入而重置。

例如,有一个页面a.html,其代码如下:

JavaScript跨域概述 

再看b.html页面的代码:

 JavaScript跨域概述

a.html页面载入3秒后跳转至b.html页面,结果为:

 JavaScript跨域概述

可以看到,在b.html页面上成功获取了其上一个页面a.html设置的window.name值。如果在之后所有载入的页面中未修改window.name,则所有这些页面获取到的window.name值均为a.html页面设置的值。当然,如有需要,其中任何页面均可修改window.name的值。注意,window.name的值只能是字符串形式,该字符串大小最大允许2M左右甚至更大,具体取决于不同浏览器,但通常足够使用。

上述示例中使用的页面a.html和b.html处于同一域,但即使a.html与b.html处于不同域,上述结论同样适用,这正是利用window.name进行跨域的原理。

接下来看具体如何通过window.name跨域获取数据。举例说明。

假设有一个www.example.com/a.html页面,需要通过a.html中的JavaScript获取另一个位于不同域上的页面www.cnblogs.com/data.html中的数据。

data.html页面的代码很简单,即为当前window.name设置a.html页面所需的数据值。data.html的代码如下:

 JavaScript跨域概述

那么在a.html页面中,如何载入data.html页面呢?显然不能直接在a.html中通过改变window.location来载入data.html,因为我们希望a.html页面不跳转也能获取data.html中的数据。答案是在a.html中使用一个隐藏的iframe充当中间人角色,由iframe获取data.html的数据,然后a.html再获取iframe获取到的数据。

充当中间人的iframe要获取data.html通过window.name设置的数据,只需将该iframe的src设为www.cnblogs.com/data.html即可。然后a.html要获取iframe所获取的数据,即获取iframe的window.name值,还必须将该iframe的src设为与a.html页面同域,否则根据前述同源策略,a.html无法访问iframe中的window.name属性。这就是整个跨域过程。

查看a.html页面的代码:

 JavaScript跨域概述

上述代码仅为最简单的原理演示,你可以使用JavaScript封装上述过程,例如动态创建iframe、动态注册各种事件等。当然,为了安全,获取数据后还可以销毁作为代理的iframe。网上也有许多类似的现成代码,有兴趣者可自行查找。

通过window.name进行跨域,即如此操作。

HTML5 postMessage跨域

四、使用HTML5中新引进的window.postMessage方法来跨域传送数据

window.postMessage(message,targetOrigin)方法是HTML5新引入的特性,可用于向其他window对象发送消息,无论该window对象是否同源。目前IE8+、Firefox、Chrome、Opera等浏览器均已支持window.postMessage方法。

调用postMessage方法的window对象是指要接收消息的那个window对象,该方法的第一个参数message为要发送的消息,类型只能是字符串;第二个参数targetOrigin用来限定接收消息的那个window对象所在的域,若不想限定域,可使用通配符*。

需要接收消息的window对象,可通过监听自身的message事件来获取传来的消息,消息内容存储在该事件对象的data属性中。

上述向其他window对象发送消息,实际上指一个页面包含多个框架的情况,因为每个框架都有一个window对象。在讨论第二种方法时,我们提到不同域的框架间可以获取对方的window对象,且可使用window.postMessage方法。下面看一个简单的示例,有两个页面:

 JavaScript跨域概述

 JavaScript跨域概述

我们运行a页面后得到的结果:

 JavaScript跨域概述

可以看到,b页面成功收到了消息。

使用postMessage来跨域传送数据较为直观和方便,但缺点是IE6、IE7不支持,因此是否使用需根据实际需求决定。

结语:

除上述几种方法外,还有Flash、在服务器上设置代理页面等跨域方式,此处不再赘述。

以上四种方法,可根据项目实际情况选择应用。个人认为window.name的方法既不复杂,也能兼容几乎所有浏览器,这确实是一种极佳的跨域方法。

以上就是本文介绍的JavaScript跨域方法、原理以及出现问题解决方法的全部内容,希望对大家有所帮助。

作者最新文章