本文承接 上一篇 博文,上一篇博文介绍了跨域的相关概念、测试demo的相关配置和
JSONP
和CORS
两种跨域方式的实现。本文主要介绍document.domain
、URL.hash
、cross-fragment
、window.name
和postMessage
这五种方式的跨域实现。html
若是 A
源和 B
源具备相同的父域名,经过设置 document.domain
属性,就很容易使其相互通讯。在 HTML 规范中document.domain
是一个只读属性,现代浏览器容许将其设置为父域名(不是顶级域名)。例如,一个是 www.myapp.com
的页面,能够设置为 myapp.com
,而另外一个来自 sample.myapp.com
的页面也能够设置为 myapp.com
,下图展现了document.domain
的工做原理:html5
经过将不一样子域名的 document.domain
属性设置为相同的父域名,来实现不一样子域名之间的跨域通讯,这并不属于同源策略限制的范畴。可是,严格来讲,子域名跨域的解决方案最适用于内部应用之间的跨域通讯。git
访问页面:document_domain_test.ejsgithub
<button>测试</button> <div></div> <iframe id="ifr" src="http://sample.myapp.com/document_iframe"></iframe> <script> document.domain = "myapp.com"; function sayHello(str) { document.querySelector('div').innerHTML = str; } document.querySelector('button').onclick = function() { document.querySelector('#ifr').contentWindow.sayHello('Hello son'); } </script>
跨域iframe:document_iframe.ejs跨域
document.domain = 'myapp.com'; function sayHello(str) { document.querySelector('div').innerHTML = str; parent.sayHello('Hello father'); }
效果:浏览器
一个 URL
由下图所示的几个部分组成:安全
通常来讲, URL
的任何改变都从新会加载一个新的网页,除了 hash
的变化, hash
的任何改变都不会致使页面刷新。 hash
已经被普遍使用在支持局部刷新的 SPA
应用中,用来记录用户的访问路径。在跨域解决方案中, hash
也很是有用,来自不一样源的页面能够相互设置对方的 URL
,包括 hash
值,但仍被限制获取对方的 hash
值。文档之间能够经过 hash
来相互通讯。以下图所示的例子:app
访问页面:url_hash_test.ejsdom
<button>发送消息</button> <iframe id="ifr" src="http://www.otherapp.com/hash_iframe"></iframe> <script> var target = "http://www.otherapp.com/hash_iframe"; function sendMsg(msg) { var data = {msg: msg}; var src = target + "#" + JSON.stringify(data); document.getElementById('ifr').src = src; } document.querySelector('button').onclick = function() { sendMsg("Time: " + (new Date())); } </script>
跨域iframe:hash_iframe.ejside
<div></div> <script> var oldHash = ""; var target = "http://www.myapp.com/url_hash_test"; var checkMessage = function() { var newHash = window.location.hash; if (newHash.length > 1) { newHash = newHash.slice(1, newHash.length); if (newHash !== oldHash) { oldHash = newHash; var msgs = JSON.parse(newHash); var msg = msgs.msg; sendMessage("Hello-father"); document.querySelector('div').innerHTML = msg; } } } setInterval(checkMessage, 1000); function sendMessage(msg) { var hash = "msg=" + msg; parent.location.href = target + "#" + hash; } </script>
主页面经过改变 iframe
的 hash
值向 iframe
传递参数, iframe
不断获取本身的 hash
值,一旦发生变化就当即显示主页面传来的消息,而且经过设置主页面的 hash
就能够像主页面传递消息了,这样实际就能够完成双向的跨域通讯了。
效果:
因为许多网站的 hash
已经被用于其余用途,对于这样的网站用 hash
跨域将很是复杂(须要从 hash
中合并和分离出消息)。所以咱们就有了基于 hash
的一个升级版: cross-fragment
,其原理以下图所示:
访问页面:cross_fragment_test.ejs
<button>发送消息</button> <div></div> <iframe id="otherapp" name="otherapp" src="http://www.otherapp.com/fragment_iframe"></iframe> <script> function sendMsg(msg) { var frame = document.createElement("iframe"); var baseProxy = "http://www.otherapp.com/fragment_req_proxy"; var request = {frameName: "otherapp", data: msg}; frame.src = baseProxy + "#" + encodeURI(JSON.stringify(request)); frame.style.display = "none"; document.body.appendChild(frame); } // 响应处理函数 function getResponse(data) { document.querySelector('div').innerHTML = "收到结果:" + data; } document.querySelector('button').onclick = function() { // 发送一个随机数 sendMsg(Math.random()); } </script>
请求代理页面:fragment_req_proxy.ejs
<script> var hash = window.location.hash; if (hash && hash.length > 1) { var request = hash.slice(1, hash.length); var obj = JSON.parse(decodeURI(request)); var data = obj.data; //目标页面的getData方法 parent.frames[obj.frameName].getData(data); } </script>
跨域iframe:fragment_iframe.ejs
<div></div> <script> function getData(data) { document.querySelector('div').innerHTML = "收到的消息:" + data; var frame = document.createElement("frame"); var baseProxy = "http://www.myapp.com/fragment_res_proxy"; // 将接收的数据扩大一百倍 var request = {data: data * 100}; frame.src = baseProxy + "#" + encodeURI(JSON.stringify(request)); frame.style.display = "none"; document.body.appendChild(frame); } </script>
响应代理页面:fragment_res_proxy.ejs
<script> var hash = window.location.hash; if (hash && hash.length > 1) { var request = hash.slice(1, hash.length); var obj = JSON.parse(decodeURI(request)); var data = obj.data; //主页面的getResponse方法 parent.parent.getResponse(data); } </script>
这个例子中先在主页面(来自myapp)放置一个otherapp下的 iframe
(目标页面),点击按钮后就在主页面中构造一个隐藏的iframe
(和目标页面同域,来自otherapp,请求代理页面),经过这个请求代理页面调用目标页面的 getData
方法,这个方法接收传来的数据,处理完成后,构造一个隐藏的 iframe
(和主页面同域,来自myapp,响应代理页面),经过响应代理页面调用主页面中的 getResponse
方法。
效果:
以前整理过一个利用该方法实现 iframe自适应 的博文,有兴趣的能够看一下
window.name
跨域是一个巧妙的解决方案,咱们通常使用 window.name
的状况以下:
window.frames[windowName]
获得一个子窗口target
属性加载任何页面 window.name
的值始终保持不变。因为 window.name
这个显著的特色,使其适用于在不一样源之间进行跨域通讯,但这是个不经常使用的属性。那么怎么在同源策略下使用呢?下图显示了如何使用 window.name
来跨域通讯。
当页面 A
想要从另外一个源获取资源或Web服务,首先在本身的页面上建立一个隐藏的 iframe
B,将 B 指向外部资源或服务,B 加载完成以后,将把响应的数据附加到 window.name
上。因为如今 A 和 B 还不一样源,A 依旧不能获取到 B 的 name
属性。当 B 获取到数据以后,再将页面导航到任何一个与 A 同源的页面,这时 A 就能够直接获取到 B 的 name
属性值。当 A 获取到数据以后,就能够随时删掉 B。
访问页面:window_name_test.ejs
<button>发送消息</button> <div class="req"></div> <div class="res"></div> <script> function sendMsg(msg) { var state = 0, data; var frame = document.createElement("frame"); var baseProxy = "http://www.otherapp.com/window_iframe"; var request = {data: msg}; frame.src = baseProxy + "#" + encodeURI(JSON.stringify(request)); frame.style.display = "none"; frame.onload = function() { if (state === 1) { data = frame.contentWindow.name; document.querySelector('.res').innerHTML = "得到响应:" + data; // 删除iframe frame.contentWindow.document.write(''); frame.contentWindow.close(); document.body.removeChild(frame); } else { state = 1; frame.src = "http://www.myapp.com/window_iframe"; } }; document.body.appendChild(frame); } document.querySelector('button').onclick = function() { var val = Math.random(); sendMsg(val); document.querySelector('.req').innerHTML = "请求数据:" + val; } </script>
跨域iframe:window_iframe.ejs
<script> var hash = window.location.hash; if (hash && hash.length > 1) { var request = hash.slice(1, hash.length); var obj = JSON.parse(decodeURI(request)); var data = obj.data; window.name = data*100; } </script>
效果:
HTML5 规范中的新方法 window.postMessage(message, targetOrigin)
能够用于安全跨域通讯。当该方法被调用时,将分发一个消息事件,若是窗口监听了相应的消息,窗口就能够获取到消息和消息来源。以下图所示:
若是 iframe
想要通知不一样源的父窗口它已经加载完成,可使用 window.postMessage
来发送消息。同时,它也将监听回馈消息。
访问页面:postMessage_test.ejs
<div></div> <iframe name="otherApp" id="otherApp" src="http://www.otherapp.com/postMessage_iframe"></iframe> <script> function handleReceive(event) { if (event.origin != "http://www.otherapp.com") return; // 处理数据 var data = JSON.parse(event.data); document.querySelector('div').innerHTML = "来自iframe的颜色:" + data.color; var otherAppFrame = window.frames["otherApp"]; otherAppFrame.postMessage("ok", "http://www.otherapp.com"); } window.addEventListener("message", handleReceive, false); </script>
跨域iframe:postMessage_iframe.ejs
<div></div> <script> function postMessage(msg) { var targetWindow = parent.window; targetWindow.postMessage(msg, "*"); } function handleReceive(msg) { if (msg.data === "ok") { document.querySelector('div').innerHTML = "消息发送成功"; } else { postMessage(JSON.stringify({color: 'red'})); } } window.addEventListener("message", handleReceive, false); window.onload = function() { postMessage(JSON.stringify({color: 'red'})); } </script>
效果:
本文介绍了经常使用的五种跨域实现方法,测试源码 请戳这 !