【每日一题】如何实现跨页面通信?
共 5569字,需浏览 12分钟
·
2021-08-28 13:03
人生苦短,总需要一点仪式感。比如学前端~
同源
即同源策略(same-origin policy) 可点击查看原文
通常,对于两个不同页面的脚本,只有当执行它们的页面位于具有相同的协议(通常为https),端口号(443为https的默认值),以及主机 (两个页面的模数 Document.domain设置为相同的值) 时,这两个脚本才能相互通信。
同源页面之间的通信
支持同源页面之间通信的方法有:
BroadCastChannel Service Worker LocalStorage Shared Worker IndexedDB window.open() + window.opener
BroadCast Channel
BroadcastChannel
“广播频道”接口代理了一个命名频道,可以让指定 origin 下的任意 browsing context 来订阅它。
它允许同源的不同浏览器窗口,Tab页,frame或者 iframe 下的不同文档之间相互通信。
通过触发一个 message 事件,消息可以广播到所有监听了该频道的 BroadcastChannel 对象。
它与window.postMessage的区别:
BroadcastChannel只能用于同源的页面之间进行通信,而window.postMessage却可以用于任何的页面之间,
此特性在 Web Worker 中可用
MDN:https://developer.mozilla.org/zh-CN/docs/Web/API/BroadcastChannel
Service Worker
这是一个实验性的功能ServiceWorker API
的 ServiceWorker接口 提供一个对一个服务工作者的引用。多个浏览上下文(例如页面,工作者等)可以与相同的服务工作者相关联,每个都通过唯一的ServiceWorker对象。
MDN:https://developer.mozilla.org/zh-CN/docs/Web/API/ServiceWorker
LocalStorage
localStorage
存储的数据可以长期保留,只读的localStorage 属性允许你访问一个Document 源(origin)的对象 Storage。
另外,localStorage 中的键值对总是以字符串的形式存储 (意味着数值类型会自动转化为字符串类型)
应注意,他受同源策略的影响。
当出现SecurityError
错误,说明请求违反了一个策略声明,或者源( origin )不是 一个有效的协议/域名/端口 (例如如果origin使用 file: 或者 data: 形式将可能发生)。比如,用户可以有对浏览器指定的origin存留数据禁用/允许的配置。
MDN: https://developer.mozilla.org/zh-CN/docs/Web/API/Window/localStorage
Shared Worker
SharedWorker
接口代表一种特定类型的 worker,可以从几个浏览上下文中访问,例如几个窗口、iframe 或其他 worker。它们实现一个不同于普通 worker 的接口,具有不同的全局作用域。
注意:如果要使 SharedWorker 连接到多个不同的页面,这些页面必须是同源的(相同的协议、host 以及端口)。
MDN: https://developer.mozilla.org/zh-CN/docs/Web/API/SharedWorker
IndexedDB
IndexedDB
是一个事务型数据库系统,类似于基于 SQL 的 RDBMS。但他是一个是一个基于 JavaScript 的面向对象数据库,用于在客户端存储大量的结构化数据(也包括文件/二进制大型对象(blobs))。
我们也可以用它储存离线数据。作为一种底层 API,IndexedDB使用索引实现对数据的高性能搜索。
注意:正如大多数的 web 储存解决方案一样,IndexedDB 也遵守同源策略。因此当你在某个域名下操作储存数据的时候,你不能操作其他域名下的数据。
此特性在 Web Worker 中可用
MDN:https://developer.mozilla.org/zh-CN/docs/Web/API/IndexedDB_API
window.open() + window.opener
window.open()
Window 接口的 open()
方法,创建一个新的浏览器窗口对象,如同使用文件菜单中的新窗口命令一样。
用指定的名称将指定的资源加载到浏览器上下文(窗口 window ,内嵌框架 iframe 或者标签 tab )。
如果没有指定名称,则一个新的窗口会被打开并且指定的资源会被加载进这个窗口的浏览器上下文中。
注意:调用window.open()方法以后,远程 URL 不会被立即载入,载入过程是异步的。
MDN:https://developer.mozilla.org/zh-CN/docs/Web/API/Window/open
window.opener
返回打开当前窗口的那个窗口的引用
例如:在window A中打开了window B,B.opener 返回 A.
如果当前窗口是由另一个窗口打开的, window.opener保留了那个窗口的引用. 如果当前窗口不是由其他窗口打开的, 则该属性返回 null.
MDN: https://developer.mozilla.org/zh-CN/docs/Web/API/Window/opener
DOM Level 0 不属于任何标准,不用考虑兼容性直接用!
非同源页面之间的通信
可以跨不同源页面进行通信的方法有:
jsonp window.postmessage() iframe
jsonp
JSONP(JSON with Padding)
是资料格式JSON的一种“使用模式”,可以让网页从别的网域获取资料。
由于同源策略,一般来说位于server1.example.com的网页无法与 server2.example.com的服务器沟通,而HTML的 <script>元素是一个例外。利用 <script>元素的这个开放策略,网页可以得到从其他来源动态产生的JSON资料,而这种使用模式就是所谓的 JSONP。
为了让浏览器可以在 <script>元素运行,从src里URL 回传的必须是可执行的JavaScript。在JSONP的使用模式里,该URL回传的是由函数调用包起来的动态生成JSON,这就是JSONP的 “填充(padding)” 或是 “前辍(prefix)” 的由来。
使用:
惯例上浏览器提供回调函数的名称当作送至服务器的请求中命名查询参数的一部分,例如:
<script type="text/javascript" src="http://server2.example.com/RetrieveUser?UserId=1823&jsonp=parseResponse">
</script>
服务器会在传给浏览器前将JSON数据填充到回调函数(parseResponse)中:
parseResponse({"Name": "小明", "Id" : 1823, "Rank": 7})
浏览器得到parseResponse函数里传入的实参,就是真实的数据。
安全:
使用远程网站的script标签会让远程网站得以注入任何的内容至网站里。如果远程的网站有JavaScript注入漏洞,原来的网站也会受到影响。
粗略的JSONP部署很容易受到跨站请求伪造(CSRF/XSRF)的攻击。因为HTML <script>标签在浏览器里不遵守同源策略,恶意网页可以要求并获取属于其他网站的JSON资料。这可能泄漏用户的密码或是其他敏感资料。不过当JSON资料不涉密、或者可以保证服务器专有性的情况下,能避免这个问题。
wiki: https://zh.wikipedia.org/wiki/JSONP
另一个解决跨域这个问题的新方法是跨域资源共享(Cross-origin resource sharing,缩写:CORS)。
window.name
在某些框架里(如,SessionVars 和 Dojo's dojox.io.windowName) ,该属性也被用于作为 JSONP 的一个更安全的备选,来提供跨域通信(cross-domain messaging)。
现代 web 应用应使用 postMessage API 进行敏感的跨域通信。
window.postmessage()
window.postMessage()
方法提供了一种受控机制来规避同源策略的限制,只要正确的使用,这种方法就可以很安全地实现跨源通信。
在窗口上调用 targetWindow.postMessage() 方法分发一个 MessageEvent 消息
otherWindow.postMessage(message, targetOrigin, [transfer]);
其他window可以监听分发的message:
window.addEventListener("message", receiveMessage, false);
function receiveMessage(event){
var origin = event.origin
if (origin !== "http://example.org:8080")
return;
// ...
}
安全
当您使用postMessage将数据发送到其他窗口时,始终指定精确的目标origin,而不是“*”
如果您确实希望从其他网站接收message,请始终使用origin和source属性验证发件人的身份。任何窗口(包括例如http://evil.example.com)都可以向任何其他窗口发送消息,并且您不能保证未知发件人不会发送恶意消息。无法检查origin和source属性会导致跨站点脚本攻击。
验证身份后,您仍然应该始终验证接收到的消息的语法。否则,您信任只发送受信任邮件的网站中的安全漏洞可能会在您的网站中打开跨网站脚本漏洞。
MDN:https://developer.mozilla.org/zh-CN/docs/Web/API/Window/postMessage
iframe
iframe 是 HTML内联框架元素 (<iframe>
), 表示嵌套的browsing context
。会被包含在 window.frames 伪数组(类数组的对象)中。
它能够将另一个HTML页面嵌入到当前页面中。
每个嵌入的浏览上下文(embedded browsing context)都有自己的会话历史记录(session history)和DOM树。
包含嵌入内容的浏览上下文称为父级浏览上下文。
顶级浏览上下文(没有父级)通常是由 Window 对象表示的浏览器窗口。
页面上的每个iframe都需要增加内存和其它计算资源,这是因为每个浏览上下文都拥有完整的文档环境
在框架内部,脚本可以通过 window.parent
引用父窗口对象。
脚本访问框架内容必须遵守同源策略,并且无法访问非同源的 window 对象的几乎所有属性。
同源策略同样适用于子窗体访问父窗体的 window 对象。
对于非同源页面,则可以通过嵌入同源iframe 作为"桥梁",将非同源页面通信转换为同源页面通信。
跨域通信可以通过 window.postMessage 来实现。
MDN:https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/iframe
总结
广播模式:
Broadcast Channe Service Worker LocalStorage+StorageEvent
共享存储模式:
Shared Worker IndexedDB cookie
口口相传模式:
window.open + window.opener
基于服务端:
Websocket Comet SSE
让我们一起携手同走前端路!