postMessage及webSocket跨域方案详解
作者:mouche
这篇文章主要为大家介绍了postMessage及webSocket跨域方案详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
一、postMessage
我们在上一篇小白也能搞懂的JSONP和CORS跨域方案已经说过两种跨域方案了,这一篇就再继续讲讲postMessage
和websocket
这两种方案,它们也能算得上是跨域方案🤓
✍是什么
- window.postMessage() 方法提供了一种受控机制来规避此限制,只要正确的使用,这种方法就很安全
- 听听MDN的解释:一个窗口可以获得对另一个窗口的引用,然后在窗口上调用
targetWindow.postMessage()
方法分发一个MessageEvent
消息。接收消息的窗口可以根据需要自由处理此事件
✍语法
targetWindow.postMessage(message, targetOrigin, [transfer]);
targetWindow
: 其他窗口的一个引用,比如iframe
的contentWindow
属性、执行window.open
返回的窗口对象、或者是命名过或数值索引的window.frames (en-US)
message
: 将要发送到其他 window 的数据。它将会被结构化克隆算法 (en-US)序列化。这意味着你可以不受什么限制的将数据对象安全的传送给目标窗口而无需自己序列化。targetOrigin
: 通过窗口的 origin 属性来指定哪些窗口能接收到消息事件,其值可以是字符串*
(表示无限制)或者一个 URI。在发送消息的时候,如果目标窗口的协议、主机地址或端口这三者的任意一项不匹配它提供的值,那么消息就不会被发送;只有三者完全匹配,消息才会被发送。这个机制用来控制消息可以发送到哪些窗口;transfer
可选: 是一串和 message 同时传递的Transferable
对象。这些对象的所有权将被转移给消息的接收方,而发送一方将不再保有所有权。
通过以上叙述我们能够了解到它的作用就是可以安全地给目标窗口发送自定义的信息
✍怎么用
有信息的发送,自然也要有信息的接收,我们可以采用addEventLister
,监听message
事件: 该事件接收到消息时触发
- 我们先在同一个窗口对其进行测试(在
Origin
值为http://127.0.0.1:5500/
的页面打开)
const data = { name : '某车', like: '前端' } //传入的message为data,targetOrigin为http://127.0.0.1:5500/ window.postMessage(data, 'http://127.0.0.1:5500/'); window.addEventListener('message',(event)=>{ //监听该回调事件并打印 console.log(event); })
- 我们看一下打印台,可以看到里面的
data
正是我们传入的数据,origin
是发送消息的源,source
为发送消息的窗口引用(后面这两个,用于我们回发消息,实现消息互通)
✍如何跨域
上述我们提到了其他窗口的引用可以是iframe
的contentWindow
属性,也可以是window.open
的返回值那么我们就都来试试看
iframe + postMessage
- 这里是使用
3300端口
父页面向内嵌子页面3301端口
发送消息
//a.html <!-- 使用iframe,src指向3301端口 --> <iframe src="http://127.0.0.1:3301/b.html" id="frame" onload="load()"></iframe> <script> const data = { name : '某车', like: '前端' } const load = function(){ //负责发布消息 let frame = document.getElementById('frame'); const targetWindow = frame.contentWindow;//得到目标窗口的引用 targetWindow.postMessage(data, 'http://127.0.0.1:3301'); //发送新消息 //也监听信息 window.onmessage = function(event) { console.log(event.data) } } </script>
- 这里记得在
onload
事件里面去触发,否则会报如下错误
- 在
3301端口
监听message
事件,然后通过event.source
得到3300端口
页面的引用,event.origin
获取3300
端口的源,然后可以回发消息
//b.html window.addEventListener('message',(event)=>{ console.log(event.data); event.source.postMessage('我可以回发消息给你', event.origin); })
3300
端口页面的打印 (图片端口号打错了,懒得修改了..)
window.open()+postMessage
先认知一下window.open()
语法
window.open(url, [name], [configuration])
url
:为要新打开页面的urlname
:为新打开窗口的名字,可以通过此名字获取该窗口对象configuration
:为新打开窗口的一些配置项,比如是否有菜单栏、滚动条、长高等等信息
还是上面的思路,我们让3300端口
给3301端口
发送信息
//a.html <button class="openWindow">打开窗口</button> <script> const btn = document.querySelector('.openWindow'); const data = { name : '某车', like: '前端' } //点击之后,执行window.open() btn.addEventListener('click', ()=>{ const targetWindow = window.open('http://127.0.0.1:3301/b.html', '3001端口'); //拿到目标窗口的引用 setTimeout(()=>{ targetWindow.postMessage(data, 'http://127.0.0.1:3301/'); //发送数据 },1000) }) //同时监听message事件 window.addEventListener('message',(event)=>{ console.log(event.data); }) </script>
3301
端口监听message
事件,并且回发信息
//b.html window.addEventListener('message',(event)=>{ console.log(event.data); event.source.postMessage('我可以回发消息给你', event.origin); })
- 看一下控制台结果
3301
端口打印如下
3300
端口打印如下
✍兼容性
还是在IE有兼容问题
小结:postMessage()方法允许来自不同源的脚本进行有限的通信,只要能获取到源和窗口对象就可以实现跨域消息传递
二、webSocket
webSocket
是一种在单个TCP连接上进行全双工通信的协议,它实现了浏览器与服务器全双工通信,能更好的节省服务器资源和带宽并达到实时通讯的目的WebSocket
和HTTP
都是应用层协议,都基于 TCP 协议- 只需要一次握手(使用HTTP协议)客户端和服务端就可以建立持久性的链接,进行双向数据传输
✍出现的原因
- 需要实现“聊天室”,“消息推送”,“实时动态”等功能
- 曾经的方案:
- 短轮询;每隔一段时间就询问一次服务器是否有新的消息,缺点就是有一定的延迟,浪费资源
- 长轮询:客户端发送请求后如果数据没有更新的话服务器就先将其挂起,有新消息则传回,等传回后又重新发起请求等待数据更新,缺点就是服务器需要保持大量的连接
✍连接流程
- 客户端先用
Upgrade:Websocket
请求头的HTTP请求,向服务器发起握手请求
Connnection:Upgrade //表示要升级协议 Upgrade: websocket //表示要升级为websocket协议 Sec-webSocket-Version:13 //版本
- 握手成功后,即升级协议,两端就可以相互传递消息了
✍兼容性
为什么说它是解决跨域的方案:因为它本身就不受同源策略的限制,客户端可以和任意服务器之间进行通信
跨域小结:
- JSONP因为仅支持get请求,淘汰无人问津是迟早的事情
- CORS支持所有类型的HTTP请求,是跨域HTTP请求的根本解决方案,但是我们上一篇也提了,还是有IE兼容问题
- postMessage方法允许来自不同源的脚本进行有限的通信,只要能获取到源和窗口对象就可以实现跨域消息传递
- websocket本身不受同源策略的限制,可以在不同源间进行通信
- 最后还有nginx反向代理,它可以说基本没啥毛病?不过我不怎么了解它就不介绍啦🤡
以上就是postMessage及webSocket跨域方案详解的详细内容,更多关于postMessage webSocket跨域的资料请关注脚本之家其它相关文章!