web messaging与Woker分类:漫谈postMessage跨线程跨页面通信
web messaging
跨文档通信(cross-document messaging):跨就是我们国内更为熟知的HTML5 window.postMessage()应用的那种通信;
通道通信(channel messaging): 伴随着server-sent事件以及web sockets, 跨文档通信和通道通信成为HTML5 通信接口“套件”中有用的一部分。
window.postMessage
window.postMessage() 方法可以安全地实现跨源通信。
iframe_contentWindow.postMessage(message, targetOrigin, [transfer]);
message发送的数据,它将会被结构化克隆算法序列化。符串,结构对象、数据对象(如:File和ArrayBuffer)或是数组都是可以的。
targetOrigin:接受方。通过窗口的origin属性来指定哪些窗口能接收到消息事件,字符串"*"(表示无限制)或者指定URI。
transfer:Transferable 对象。 这些对象的所有权将被转移给消息的接收方,而发送一方将不再保有所有权。
其他window可以监听分发的message:window.addEventListener("message", callback, false);
window.postMessage安全问题
如果您不希望从其他网站接收message,请不要为message事件添加任何事件侦听器。 这是一个完全万无一失的方式来避免安全问题。
如果您确实希望从其他网站接收message,请始终使用origin和source属性验证发件人的身份。无法检查origin和source属性会导致跨站点脚本攻击。—— 任何窗口都可以向任何其他窗口发送消息,并且您不能保证未知发件人不会发送恶意消息。 但是,验证身份后,您仍然应该始终验证接收到的消息的语法。 否则,您信任只发送受信任邮件的网站中的安全漏洞可能会在您的网站中打开跨网站脚本漏洞。
使用postMessage将数据发送到其他窗口时,始终指定精确的目标origin,而不是*。
无法检查origin和source属性会导致跨站点脚本攻击。
worker.postMessage
Worker 接口是Web Workers API的一部分,代表一个后台任务,创建一个专用Web worker,它只执行URL指定的脚本,并且在工作线程中执行。主从线程通过 postMessage发送消息和 onmessage onmessage 接受消息
worker 将运行在与当前 window不同的另一个全局上下文中,这个上下文由一个对象表示,标准情况下为DedicatedWorkerGlobalScope (标准 workers 由单个脚本使用; 共享workers使用SharedWorkerGlobalScope)。
除了无法读取DOM对象(包括:document、window、parent)、本地文件、对话框(alert/confirm/prompt),大部分 window 对象的方法和属性是可以使用的,如: WebSockets、IndexedDB、 XMLHttpRequest 等,具体查看 Functions and classes available to workers
Woker分类
Shared Workers 一个Shared Workers可以被多个脚本使用——即使这些脚本正在被不同的window、iframe或者worker访问。,只要这些workers处于同一主域。共享worker 比专用 worker 稍微复杂一点 — 脚本必须通过活动端口进行通讯(MessageChannel的port1与port2间传递 )。详情请见
SharedWorker。Broadcast Channel: 可以实现同 源 下浏览器不同窗口,Tab页,frame或者 iframe 下的 浏览器上下文 (通常是同一个网站下不同的页面)之间的简单通讯。
在任何页面new BroadcastChannel('mychannel')并postMessage,其他页面的BroadcastChannel实例onmessage 都能收收到消息
它与postMessage的区别就是:BroadcastChannel只能用于同源的页面之间进行通信,而postMessage却可以用于任何的页面之间的通信,换句话说,BroadcastChannel可以认为是postMessage的一个实例,它承担了postMessage的一个方面的功能。
Service Workers 一般作为web应用程序、浏览器和网络(如果可用)之间的代理服务。他们旨在(除开其他方面)创建有效的离线体验,拦截网络请求,以及根据网络是否可用采取合适的行动,更新驻留在服务器上的资源。他们还将允许访问推送通知和后台同步API。
Service worker运行在worker上下文,因此它不能访问DOM。相对于驱动应用的主JavaScript线程,它运行在其他线程中,所以不会造成阻塞。它设计为完全异步,同步API(如XHR和localStorage)不能在service worker中使用。
不同于普通Worker,Service Worker 是一个浏览器中的进程而不是浏览器内核下的线程(Service Worker是走的另外的线程,可以理解为在浏览器背后默默运行的一个线程,或者说是独立于当前页面的一段运行在浏览器后台进程里的脚本。)因此它在被注册安装之后,能够被在多个页面中使用,也不会因为页面的关闭而被销毁。
出于对安全问题的考虑,Service Worker 只能被使用在 https 或者本地的 localhost 环境下。
subworker: worker 能够生成更多的 worker。这就是所谓的subworker(还是Woker),它们必须托管在同源的父页面内。而且,subworker 解析 URI 时会相对于父 worker 的地址而不是自身页面的地址。这使得 worker 更容易记录它们之间的依赖关系。
Chrome Workers: It works exactly like a standard Worker,you can do so by using ChromeWorker instead of the standard Worker object。我的理解是 只是在chrome 跑的worker 。详情请见ChromeWorker
Woker作用
Worker作用在《浏览器层面优化前端性能(1):Chrom组件与进程/线程模型分析》里面讲过
要尽量避免JS执行时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞的感觉。Web Worker 异步优化下》
创建Worker时,JS引擎向浏览器申请开一个子线程(子线程是浏览器开的,完全受主线程控制,而且不能操作DOM)
JS引擎线程与worker线程间通过特定的方式通信(postMessage API,需要通过序列化对象来与线程交互特定的数据)
JS引擎是单线程的,这一点的本质仍然未改变,Worker可以理解是浏览器给JS引擎开的外挂,专门用来解决那些大量计算问题。
SharedWorker是浏览器所有页面共享的,不能采用与Worker同样的方式实现,因为它不隶属于某个Render进程,可以为多个Render进程共享使用。所以Chrome浏览器为SharedWorker单独创建一个进程来运行JavaScript程序,在浏览器中每个相同的JavaScript只存在一个SharedWorker进程,不管它被创建多少次。
页面A发送数据给worker:window.worker.port.postMessage('get'),然后打开页面B,调用window.worker.port.postMessage('get'),即可收到页面A发送给worker的数据。
worker 属性与方法
postMessage(data, transferList);
data:发送的数据,会被 结构化克隆 ( structured clone)
transferList:Transferable对象的数组,用于传递所有权。如果一个对象的所有权被转移,在发送它的上下文中将变为不可用(中止),并且只有在它被发送到的worker中可用。
可转移对象是如ArrayBuffer,MessagePort或ImageBitmap的实例对象。transferList数组中可默认不传,但不可传入null。一般为MessageChannel port
terminate()
立即终止 Worker 的行为. 本方法并不会等待 worker 去完成它剩余的操作;worker 将会被立刻停止
onmessage(event)
Worker 接口的onmessage属性表示一个EventHandler事件处理函数,当message 事件发生时,该函数被调用。这些事件所属MessageEvent类型,且当Worker子线程返回一条消息时被调用
event: event 对象 event.data 为 structured clone 数据
onerror() onmessageerror
onmessageerror 事件处理器接口是一个EventListener, 在 MessageEvent 类型的事件 messageerror 触发时调用 — 也就是说, 它收到的消息是不能进行序列化的 deserialized.
Woker性能优化
worker.terminate()
使用完毕,为了节省系统资源,必须关闭 Worker。
// 主线程
worker.terminate();
// Worker 线程
self.close();
worker.postMessage(arrayBuffer, [arrayBuffer])
主线程与 Worker 之间的通信内容,可以是文本,也可以是对象。需要注意的是,这种通信是拷贝关系,即是传值而不是传址,Worker 对通信内容的修改,不会影响到主线程。事实上,浏览器内部的运行机制是,先将通信内容串行化,然后把串行化后的字符串发给 Worker,后者再将它还原。这会造成性能问题!为了解决这个问题,JavaScript 允许主线程把二进制数据直接转移给子线程,但是一旦转移,主线程就无法再使用这些二进制数据了,这是为了防止出现多个线程同时修改数据的麻烦局面。这种转移数据的方法,叫做Transferable Objects。
var ab = new ArrayBuffer(1);
worker.postMessage(ab, [ab]);
对于大的数据处理,不会产生性能负担。
同页面的 Web Worker
Worker 载入的是一个单独的 JavaScript 脚本文件,但是也可以载入与主线程在同一个网页的代码。减少网络加载耗时
<script id="worker" type="app/worker">
addEventListener('message', function () {
postMessage('some message');
}, false);
</script>
<script>
var blob = new Blob([document.querySelector('#worker').textContent]);
var url = window.URL.createObjectURL(blob);
var worker = new Worker(url);
worker.onmessage = function (e) {
// e.data === 'some message'
};
</script>
先将嵌入网页的脚本代码(注意必须指定<script>标签的type属性是一个浏览器不认识的值),转成一个二进制对象,然后为这个二进制对象生成 URL,再让 Worker 加载这个 URL。
woker 在时间循环中执行顺序
worker 因为JavaScript 新开一个线程,执行worker代码。shareWoker因为不同tab(一个tab一个进程),因而新开一个进程。
// 主线程
let woker = new Worker('./test2.js');
woker.onmessage = (res) => {
console.log(res.data);
};
setTimeout(()=>{
console.log('main')
setTimeout(()=>{
console.log('main2')
setTimeout(()=>{
console.log('main3')
},0)
},0)
},0)
// woker 线程,test2.js
setTimeout(() => {
self.postMessage('woker1');
}, 0);
self.postMessage('woker2');
输出顺序为,每次都不一样,以为有网络请求呀
main main2 woker2 main3 woker1
main main2 woker2 woker1 main3
main main2 main3 woker2 woker1
如果是桶页面内,顺序就机会不会变
<script id="worker" type="app/worker">
setTimeout(() => {
self.postMessage('woker1');
}, 0);
self.postMessage('woker2');
</script> <script>
// let woker = new Worker('./test2.js');
var blob = new Blob([document.querySelector('#worker').textContent]);
var url = window.URL.createObjectURL(blob);
var worker = new Worker(url);
worker.onmessage = (res) => {
console.log(res.data);
};
setTimeout(()=>{
console.log('main')
setTimeout(()=>{
console.log('main2')
setTimeout(()=>{
console.log('main3')
},0)
},0)
},0) </script>
这个,还是JavaScript的 event loop 事件机制觉得,推荐阅读《弄懂javascript的执行机制:事件轮询|微任务和宏任务》
在浏览器环境中,常见的 macro task 有 setTimeout、MessageChannel、postMessage、setImmediate。而常见的 micro task 有 MutationObsever 和 Promise.then。需要留意的是:
MessageChannel
Vue中对于 macro task 的实现,优先检测是否支持原生 setImmediate,这是一个高版本 IE 和 Edge 才支持的特性,不支持的话再去检测是否支持原生的MessageChannel,如果也不支持的话就会降级为 setTimeout 0。
MessageChannel用法
MessageChannel创建了一个通信的管道,这个管道有两个端口[port1,port2],可以可以通过postMessage互相通信。
const channel = new MessageChannel();
let port1 = channel.port1;
let port2 = channel.port2;
port1.onmessage = function (event) {
console.log("port1收到来自port2的数据:" + event.data);
};
port2.onmessage = function (event) {
console.log("port2收到来自port1的数据:" + event.data);
};
port1.postMessage("发送给port2");
port2.postMessage("发送给port1");
MessageChannel用法很简单,但是功能却不可小觑。
MessageChannel作用
web worker间通信
多个web worker并想要在两个web worker之间实现通信的时候,MessageChannel就可以派上用场:
var w1 = new Worker("worker1.js");
var w2 = new Worker("worker2.js");
var ch = new MessageChannel();
w1.postMessage("initial port",[ch.port1]);
w2.postMessage("initial port",[ch.port2]);
w2.onmessage = function(e){
console.log(e.data);
}
通过web worker的postMessage方法把两个MessageChannel的port传递给两个web woker,然后就可以通过每个port的postMessage方法传递数据了。
iframe兄弟间通
传统iframe父子间通信:
var iframe1 = document.getElementById('iframe1');
iframe1.postMessage(message, '*');
使用MessagePort.postMessage方法把一条消息和MessageChannel.port2传递给iframe。
var iframe1 = document.getElementById('iframe1');
var iframe2 = document.getElementById('iframe2');
iframe1.contentWindow.postMessage('main','*',[port1]);
iframe2.contentWindow.postMessage('main','*',[port2]);
代码地址:channel messaging basic demo
worker_threads兄弟线程通信
nodejs的MessageChannel虽然与浏览器的,实现方式不同,但是用法相同,都是一个模型。在此列出,阐释这种思想。
const {isMainThread, parentPort, threadId, MessageChannel, Worker} = require('worker_threads');
通信事件message事件对象
Message事件的定义可参见这里
| data | 包含任意字符串数据,由原始脚本发送 |
|---|---|
| origin | 一个字符串,包含原始文档的方案、域名以及端口(如:http://domain.example:80) |
| lastEventId | 一个字符串,包含了当前的消息事件的唯一标识符。 |
| source | 原始文件的窗口的引用。更确切地说,它是一个WindowProxy对象。 |
| ports | 一个数组,包含任何MessagePort对象发送消息。 |
在跨文档通信和通道通信中,lastEventId的值一般是个空字符串;lastEventId应用在服务器端发送事件上。发送信息中如果没有ports, 则ports属性值就是个长度为0的数组。
MessageEvent继承DOM事件接口,且属性共享。然而,通信事件并没有冒泡,不能取消,也没有默认行为。
Service Worker
前端缓存分析
前端缓存 大致可以分为 http缓存 与 浏览器缓存
http缓存推荐阅读《浏览器http缓存机制剖析:存储策略与过期策略的机理分析》,我们来分析下 浏览器缓存
storage
cookie、localStorage、sessionStorage
cookie 最大约为4k,每个域名最多50kcookie——不同浏览器限制不一样,一般用来存储关键数据(比如用户登录信息)
localStorage/sessionStorage通常有5MB的存储空间,比如微信文章 不需要改动的资源(如css/js)就基本存储在localStorage里面
推荐阅读《登录状态控制:cookies对比sessionStorage保持信息的分析》
前端数据库:
WebSql和IndexDB,其中WebSql被规范废弃,他们都有大约50MB的最大容量,一般 当页面 store 的数据可以直接存储在里面。
manifest 缓存
已经被废弃,因为他的设计有些不合理的地方,他在缓存静态文件的同时,也会默认缓存html文件。这导致页面的更新只能通过manifest文件中的版本号来决定。所以,应用缓存只适合那种常年不变化的静态网站。如此的不方便,也是被废弃的重要原因。
推荐阅读《html5离线缓存manifest详解》、《HTML5离线存储实战之manifest的那些坑》
Service Worker
Service Worker本质上也是浏览器缓存资源用的,只不过他不仅仅是cache,也是通过worker的方式来进一步优化。
他基于h5的web worker,所以绝对不会阻碍当前js线程的执行,sw最重要的工作原理就是
后台线程:独立于当前网页线程;
网络代理:在网页发起请求时代理,来缓存文件;
这里不再赘述,再开一篇《ServiceWorker工作机制与生命周期:资源缓存与协作通信处理》
参考文章:
MessageChannel是什么,怎么使用? https://www.jianshu.com/p/4f07ef18b5d7
HTML5 postMessage iframe跨域web通信简介 https://www.zhangxinxu.com/wordpress/2012/02/html5-web-messaging-cross-document-messaging-channel-messaging/
Web Worker 使用教程 www.ruanyifeng.com/blog/2018/07/web-worker.html
转载本站文章《web messaging与Woker分类:漫谈postMessage跨线程跨页面通信》,
请注明出处:https://www.zhoulujun.cn/html/webfront/SGML/html5/2020_0615_8465.html
web messaging与Woker分类:漫谈postMessage跨线程跨页面通信的更多相关文章
- JNI加载Native Library 以及 跨线程和Qt通信
Part1 Java Native Interface-JNI-JAVA本地调用 JNI标准是Java平台的一部分, 允许Java代码和其他语言进行交互; 开始实现-> Step 1) 编写Ja ...
- HTML(六)HTML iframe 使用postMessage方法进行跨文档消息传递
什么是iframe HTML内联框架元素 <iframe> 表示嵌套的浏览上下文,有效地将另一个HTML页面嵌入到当前页面中. <iframe id="inlineFram ...
- 通过Html5的postMessage和onMessage方法实现跨域跨文档请求访问
在项目中有应用到不同的子项目,通过不同的二级域名实现相互调用功能.其中一个功能是将播放器作为单独的二级域名的请求接口,其他项目必须根据该二级域名调用播放器.最近需要实现视频播放完毕后的事件触发,调用父 ...
- [转]html5: postMessage解决跨域和跨页面通信的问题
[转]html5: postMessage解决跨域和跨页面通信的问题 平时做web开发的时候关于消息传递,除了客户端与服务器传值,还有几个经常会遇到的问题: 多窗口之间消息传递(newWin = wi ...
- HTML5:使用postMessage实现Ajax跨域请求
HTML5:使用postMessage实现Ajax跨域请求 由于同源策略的限制,Javascript存在跨域通信的问题,典型的跨域问题有iframe与父级的通信等. 常规的几种解决方法: (1) do ...
- 文档通信(跨域-不跨域)、时时通信(websocket)、离线存储(applicationCache)、开启多线程(web worker)
一.文档间的通信 postMessage对象 //不跨域 1.iframe:obj.contentWindow [iframe中的window对象] iframe拿到父级页面的window: pare ...
- html5 postMessage解决iframe跨协议跨域通信问题
a.html有个iframe载入b.com/login.html,当login完成时通知a.html页面登录完成并传递UserName 1.a.html 监听消息 window.addEventLis ...
- window.parent.postMessage 解决iframe父子页面域名不一样出现的跨域问题
window.parent.postMessage 解决iframe父子页面域名不一样出现的跨域问题 内嵌 iframe 页面,一般使用 window.parent 或 window.top 来获取父 ...
- 渐进式web应用开发---Service Worker 与页面通信(七)
_ 阅读目录 一:页面窗口向 service worker 通信 二:service worker 向所有打开的窗口页面通信 三:service worker 向特定的窗口通信 四:学习 Messag ...
- Web应用程序系统的多用户权限控制设计及实现-页面模块【9】
前五章均是从整体上讲述了Web应用程序的多用户权限控制实现流程,本章讲述Web权限管理系统的基本模块-页面模块.页面模块涉及到的数据表为页面表. 1.1页面域 为了更规范和方便后期系统的二次开发和维护 ...
随机推荐
- 使用 Java 对比两个PDF文档之间的差异
不论是在团队写作还是在个人工作中,PDF 文档往往会经过多次修订和更新.掌握 PDF 文档内容的变化对于管理文档有极大的帮助.通过对比 PDF 文档,用户可以快速找出文档增加.删除和修改的内容,更好地 ...
- 【爬虫实战】用Python采集任意小红书笔记下的评论,爬了10000多条,含二级评论!
目录 一.爬取目标 二.爬虫代码讲解 2.1 分析过程 2.2 爬虫代码 三.演示视频 一.爬取目标 您好!我是@马哥python说 ,一名10年程序猿. 我们继续分享Python爬虫的案例,今天爬取 ...
- java 处理常量字符串过长 & springboot 项目读取 resouces 文件夹下的文件内容
长字符串起因 项目里面有一长串的加密字符串(最长的万多个字符),需要拼接作为参数发送给第三方. 如果我们使用 枚举 定义的话,idea 编译的时候就会出现编译报错 Error: java:常量字符串过 ...
- 深度解析BERT:从理论到Pytorch实战
本文从BERT的基本概念和架构开始,详细讲解了其预训练和微调机制,并通过Python和PyTorch代码示例展示了如何在实际应用中使用这一模型.我们探讨了BERT的核心特点,包括其强大的注意力机制和与 ...
- SNN_TIPS
脉冲神经网络的研究思路: ANN2SNN 代表: 梯度下降法 代表: STDP 代表: 神经网络代差划分 以神经元实现功能为准: 优势 SNN是一个动态系统,在动态识别中发挥出色,比如语音识别和动态图 ...
- [Jetson Nano]SSH连接Jetson Nano时出现Xlib: extension NV-GLX missing on display localhost:10.0
解决SSH连接Jetson Nano时遇到的"Xlib: extension "NV-GLX" missing on display 'localhost:10.0'&q ...
- 本地MinIO存储服务Java远程调用上传文件
MinIO是一款高性能.分布式的对象存储系统,它可以100%的运行在标准硬件上,即X86等低成本机器也能够很好的运行MinIO.它的优点包括高性能.高可用性.易于部署和管理.支持多租户等. Cpola ...
- H.264 和 H.265对比
前言 H.264标准正式发布于2003年3月,距今已经20多年了,但它仍然是当下最流行的视频编解码标准. H.265正式发布于2013年4月.虽然H.265标准是围绕着H.264进行制定的,也保留了原 ...
- extern关键字的用法
extern关键字的理解 extern是C/C++语言中的一个关键字,用于声明一个变量或函数具有外部链接性(external linkage),即这些变量或函数可以被其他文件访问. 在C/C++中,如 ...
- Guava Preconditions类的各种用法
公众号「架构成长指南」,专注于生产实践.云原生.分布式系统.大数据技术分享. Guava Preconditions类 提供静态方法列表,用于检查是否使用有效参数值调用方法或构造函数.如果前提条件失败 ...