ThinkChat🤖让你学习和工作更高效,注册即送10W Token,即刻开启你的AI之旅 广告
[TOC] ### 前言 在多业务多平台的场景下,不可避免的会遇到业务整合或者页面整合的过程,而跨域通讯是必备的技能点,其中一个非常常见的点就是iframe自动根据载入页面高度撑大。其他方面需要跨域传值,保存页面基本信息等都离不开这项基本技术。 ### 跨域概念 > 跨域是指从一个域名的网页去请求另一个域名的资源。比如从www.baidu.com 页面去请求 www.google.com 的资源。跨域的严格一点的定义是:只要 协议,域名,端口有任何一个的不同,就被当作是跨域。可以通过以下例子更好的理解跨域概念。 (限制跨域是浏览器的问题,不是js的问题) | url |说明 | 是否允许通讯 | | --- | --- | --- | | http://www.a.com/a.js; http://www.a.com/b.js | 同一域名不同文件 | 允许 | | http://www.a.com/js/a.js; http://www.a.com/script/b.js | 同一域名不同目录 | 允许 | | http://www.a.com:8000/js/a.js; http://www.a.com/script/b.js | 同一域名,不同端口 | 不允许 | | http://www.a.com/a.js;https://www.a.com/b.js | 同一域名,不同协议 | 不允许 | | http://www.a.com/a.js;http://70.32.92.74/b.js | 域名和域名对应ip | 不允许 | | http://www.a.com/a.js;http://script.a.com/b.js | 主域相同,子域不同 |不允许 | | http://www.a.com/a.js;http://a.com/b.js | 同一域名,不同二级域名(同上)| 不允许 | |http://www.cnblogs.com/a.js;http://www.a.com/b.js | 不同域名 | 不允许 | * 备注:以下方案均以a.htm嵌入b.htm为例 ### 方案一:document.domain+iframe * 前提:主域相同,没有兼容问题 * 原理:设置为同一域名,改变识别标识 * 具体方案: 1 . 在www.a.com/a.html 中 :`document.domain = 'a.com';` 2 . 在www.script.a.com/b.html 中:`document.domain = 'a.com';` ### 方案二:动态创建script(可以忽略) * 前提:无 * 原理:因为script标签不受同源策略的限制 * 具体方案:可以动态创建,也可以直接引入其他域名下的js文件,其中的js方法也可以直接使用。 `<script src="http://www.b.com/public.js"></script>` 附:js原生手写写法 ~~~ function loadScript(url, func) { var head = document.head || document.getElementByTagName('head')[0]; var script = document.createElement('script'); script.src = url; script.onload = script.onreadystatechange = function(){ if(!this.readyState || this.readyState=='loaded' || this.readyState=='complete'){ func(); script.onload = script.onreadystatechange = null;} }; head.insertBefore(script, 0); } window.baidu = { sug: function(data){console.log(data); }} loadScript('http://suggestion.baidu.com/su?wd=w',function(){console.log('loaded')}); ~~~ ### 方案三 :window.name * window.name 可以实现跨域存储,需要借助中介界面(`/proxy.html`);存储大小2M. * 具体方案: 1. a.com/a.html ~~~ <script> //需要传入传参的页面的url ,需要数据的页面或者iframeid (可以为null) var getDomainData=function(url,contIframeId){ var state = 0, iframe = document.createElement('iframe'), loadfn = function() { if(state === 1) { var data = iframe.contentWindow.name; // 读取数据 console.log(data); var jsonData=eval("("+data+")"); var jsonData=JSON.parse(data); if(contIframeId)$("#"+contIframeId).height(data).show(); iframe.contentWindow.document.write('');//得到数据之后移除 iframe.contentWindow.close(); document.body.removeChild(iframe); } else if(state === 0) { state = 1; iframe.contentWindow.location = "/proxy.html"; // 设置的代理文件 } }; iframe.src = url; //需要传参的页面 iframe.style.display = "none"; if(iframe.attachEvent) { iframe.attachEvent('onload', loadfn); } else { iframe.onload = loadfn; } document.body.appendChild(iframe);//载入iframe } getDomainData("http://www. b.com/b.html","contIframe"); </script> ~~~ 2\. b.com/b.html : ~~~ var jsonData={ “name":value, "name2":value2 } window.name=JSON.stringify(jsonData);// 建议用json格式,也可以为字符串 ~~~ ###方案四:jsonp ,推荐方式 * 最为推荐的跨域解决方案,没有兼容问题 * 具体方案: 1. 前提:需要b.com准备一个响应的json文件或者数据,比如 ~~~ inf({ "code":"ZJ2017", "price":1788, "tickets":100 }); ~~~ 1. 原生js方式 ~~~ var script=document.createElement("script"); script.src="http://www.b.com/demo.json?callback=inf"; document.body.appendChild(script); function inf(json){ console.log(json); } ~~~ 2. ajax :jsonp方式 ~~~ $.ajax({ type:"get", async:false, url:"http://www.b.com/demo.json?callback=inf", dataType:"jsonp", jsonp:"callback",//传递给请求处理程序或页面的,用以获得jsonp回调函数名的参数名(一般默认为:callback) jsonpCallback:"inf",//为jsonp请求指定一个回调函数名 success:function(json){ console.log(json); }, error:function(){ console.log("失败"); } }); ~~~ ###方案五: location.hash + iframe * 利用location.hash来进行传值,过程如下: 1.a.html首先创建自动创建一个隐藏的iframe,iframe的src指向b.com域名下的b.html页面 2.b.html响应请求后再将通过修改a.html的hash值来传递数据 3.同时在a.html上加一个定时器,隔一段时间来判断location.hash的值有没有变化,一旦有变化则获取获取hash值 注:由于两个页面不在同一个域下IE、Chrome不允许修改parent.location.hash的值,所以要借助于a.com域名下的一个代理iframe来解决这个问题(proxy.html)。 * 方案如下: 1. a.html 中 ~~~ function startRequest(){ var ifr = document.createElement('iframe'); ifr.style.display = 'none'; ifr.src = 'http://test.promange.ucmed.cn'; document.body.appendChild(ifr); } function checkHash() { try { var data = location.hash ? location.hash.substring(1) : ''; if (console.log) { console.log('Now the data is '+data); } } catch(e) {}; } setInterval(checkHash, 2000); ~~~ 2. b.html 中 ~~~ switch(location.hash){ case '#data': callBack("data"); break; case '#paramset': //do something…… break; } function callBack(dat){ try { parent.location.hash = dat; } catch (e) { // ie、chrome的安全机制无法修改parent.location.hash, // 所以要利用一个中间的a.com域下的代理iframe var ifrproxy = document.createElement('iframe'); ifrproxy.style.display = 'none'; ifrproxy.src = 'http://www.a.com/proxy.html#'+dat; // 注意该文件在"a.com"域下 document.body.appendChild(ifrproxy); } } ~~~ 3. a.com 的 proxy.html 中 `parent.parent.location.hash = self.location.hash.substring(1);` ### 方案六:html5 postMessage ,推荐使用,灵活方便 * 建议使用,兼容性,http://caniuse.com/#search=postMessage ;现代浏览器基本都支持,优点,只有发送时,接受一方才有数据,比较灵活。 * 具体方案: 1. a.html ~~~ window.addEventListener('message', function(event){ // 通过origin属性判断消息来源地址 if (event.origin == 'http://test.ybl.ucmed.cn') { console.log(event.data); } }, false); ~~~ 2. b.html ~~~ var json=({"height":300});//推荐json格式 var targetorigin="*";//发送目标服务器 top.postMessage(json,targetorigin) //设置为顶层窗口发送消息,也可以为window ~~~ ### 方案七:websocket 实时通讯 * web sockets是一种浏览器的API,它的目标是在一个单独的持久连接上提供全双工、双向通信。(同源策略对web sockets不适用) * web sockets原理:在JS创建了web socket之后,会有一个HTTP请求发送到浏览器以发起连接。取得服务器响应后,建立的连接会使用HTTP升级从HTTP协议交换为web sockt协议。 * 具体方案: 1. a.html 创建发送请求 ~~~ var ws = new WebSocket(“ws://localhost:8080”); ws.onopen = function() { console.log(“open”); ws.send(“hello”); }; ws.onmessage = function(evt) { console.log(evt.data) }; ws.onclose = function(evt) { console.log(“WebSocketClosed!”); }; ws.onerror = function(evt) { console.log(“WebSocketError!”); }; ~~~ 2. 需要服务器b.com 写对应的响应指令,不再赘述。