SharedWorker 让你多个页面相互通信

news2024/9/21 7:04:40

SharedWorker 是一个新的Web Worker API,它允许你在多个页面之间共享一个Worker

SharedWorker 代表一种特定类型的Worker,可以在多个浏览器上下文中运行,比如多个页面或者多个iframe

什么是 SharedWorker

根据前几篇的了解,大家应该对Worker有了一定的了解,SharedWorkerWorker的一种,它允许你在多个页面之间共享一个Worker

SharedWorker对比于Service Worker要简单很多,同样也是继承与Worker,但是它的生命周期是独立于页面的。

SharedWorker和其他Worker的相同,都是需要同源的,它是通过url来区分是否是同一个SharedWorker

使用 SharedWorker

SharedWorker的使用很简单,只需要在创建Worker的时候,需要传入一个name参数,这个name参数就是SharedWorker的标识,相同的name会共享同一个SharedWorker

const sharedWorker = new SharedWorker('worker.js', 'mySharedWorker'); 

默认情况下name并不是必须的,这个时候相同的url会共享同一个SharedWorker

const sharedWorker = new SharedWorker('worker.js'); 

创建SharedWorker之后,我们就可以通过port属性来和SharedWorker进行通信了。

sharedWorker.port.onmessage = (e) => {console.log(e.data);
}; 

在主线程中,可以通过创建出来的worker对象中的port属性来和SharedWorker进行通信。

使用worker.port.onmessage来监听SharedWorker的消息。

SharedWorker中,可以通过self.onconnect来监听SharedWorker的连接。

在监听事件中会获取到与SharedWorker连接的port,通过这个port就可以和主线程进行通信了。

self.onconnect = (e) => {const port = e.ports[0];port.postMessage('hello');
}; 

通信还是通过postMessage来进行的。

通信

上面的通信只是一个简单的示例,在主线程中创建的SharedWorker,并使用port来和SharedWorker进行通信。

通常情况下,我们会使用onmessage来监听SharedWorker的消息,使用这种方式默认是打开状态,如果使用addEventListener来监听SharedWorker的消息,需要手动调用start方法来开启。

sharedWorker.port.addEventListener('message', (e) => {console.log(e.data);
});

sharedWorker.port.start(); 

在主线程中,我们可以通过port来实现和SharedWorker的通信。

// 发送消息
sharedWorker.port.postMessage('hello');

// 监听消息
sharedWorker.port.onmessage = (e) => {console.log(e.data);
};

// 或者
sharedWorker.port.addEventListener('message', (e) => {console.log(e.data);
});
sharedWorker.port.start(); 

SharedWorker中使用方式也是一样的,不同的是,我们需要通过self.onconnect来监听SharedWorker的连接。

self.onconnect = (e) => {const port = e.ports[0];// 发送消息port.postMessage('hello');// 监听消息port.onmessage = (e) => {console.log(e.data);};// 或者port.addEventListener('message', (e) => {console.log(e.data);});port.start();
}; 

可以看到上面监听的时候,获取port是一个数组,这是因为SharedWorker可以和多个页面进行通信。

每个页面都会有一个port,通过这个portSharedWorker进行通信。

通常情况下我们会记录下所有的port,然后在需要的时候进行通信。

const ports = [];

self.onconnect = (e) => {const port = e.ports[0];ports.push(port);port.onmessage = (e) => {console.log(e.data);};
};

// 发送消息
function sendMessage(message) {ports.forEach((port) => {port.postMessage(message);});
} 

上面我们的案例中,我们只创建了一个SharedWorker,所以只有一个port,但是如果我们创建了多个SharedWorker,那么就会有多个port

这里收到消息之后浏览器控制台没有任何打印信息,因为SharedWorker的作用域中没有window对象,所以consolealert等方法都是无法使用的。

如果我们需要调试SharedWorker,可以在浏览器地址栏中输入chrome://inspect/#workers,这样就可以看到当前页面中的SharedWorker

指定页面通信

上面我们拿到了所有的port,但是我们并不知道这个port是哪个页面的,所以我们需要在创建SharedWorker的时候,指定每个页面的标识。

但是很遗憾,SharedWorker并没有提供这样的功能,所以我们需要自己实现。

这里我们可以使用双数组来实现,一个数组用来存储页面的标识,一个数组用来存储port

const pages = [];
const ports = [];

self.onconnect = (e) => {const port = e.ports[0];ports.push(port);// 获取页面标识const page = Math.random().toString(36).substr(2);port.postMessage(page);pages.push(page);// 监听消息port.onmessage = (e) => {const index = ports.indexOf(port);const page = pages[index];// 这样就可以知道是哪个页面发来的消息了console.log(page);};}; 

有了上面的代码,我们就可以知道是哪个页面发来的消息了,并且也可以指定页面发送消息。

实战

上面我们已经知道了如何使用SharedWorker,那么我们就可以使用它来实现一个简单的聊天室。

这里我们使用SharedWorker来实现消息的转发,页面之间的通信使用postMessage

  • SharedWorker
const uuids = [];
const ports = [];

self.onconnect = (e) => {const port = e.ports[0];ports.push(port);// 获取页面标识const uuid = Math.random().toString(36).substr(2);uuids.push(uuid);port.postMessage({type: 'connect',uuid: uuid});// 监听消息port.onmessage = (e) => {const index = ports.indexOf(port);const uuid = uuids[index];// 群发消息broadcast({type: 'message',sender: uuid,message: e.data.message});};
};

// 群发消息
const broadcast = (data) => {ports.forEach((port) => {port.postMessage(data);});
} 
  • index.html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title><style> .chat {width: 100%;height: 100%;display: flex;flex-direction: column;}#messages {height: 500px;width: 300px;overflow: auto;border: #3498db 1px solid;padding: 0 10px;}.chat__messages {flex: 1;overflow-y: auto;margin: 4px 0;}.sender {font-size: 12px;margin: 0;padding: 0;}.sender:after {content: ' :';}.message {width: auto;max-width: 300px;background: #3498db;padding: 10px;border-radius: 10px;margin: 10px 0;font-size: 16px;color: #fff;} </style>
</head>
<body>

<div class="chat"><div id="messages"></div><div class="chat__input"><input type="text" id="message"><button id="send">发送</button></div>
</div>

<script> let uuid = ''const sharedWorker = new SharedWorker('sharedWorker.js');sharedWorker.port.onmessage = (e) => {if (e.data.type === 'connect') {uuid = e.data.uuid;alert('连接成功')return;}const messageDom = ` <div class="chat__messages"><p class="sender" ${uuid === e.data.sender ? 'style="color: #165DFF"' : ""}>${e.data.sender}</p><p class="message">${e.data.message}</p> </div>`document.getElementById('messages').insertAdjacentHTML('beforeend', messageDom);// 滚动到底部document.getElementById('messages').scrollTop = document.getElementById('messages').scrollHeight;};document.getElementById('send').addEventListener('click', () => {const message = document.getElementById('message').value;if (!message) {alert('请输入内容')return;}const data = {type: 'message',message: message,}sharedWorker.port.postMessage(data);document.getElementById('message').value = '';}); </script>
</body>
</html> 

上面的代码我们实现了一个简单的聊天室,我们可以在多个页面打开聊天室,然后在其中一个页面发送消息,其他页面都会收到消息。

上面的代码中还有一个uuid,=还可以基于这么来实现私聊,比如我们可以在页面中添加一个select,然后选择一个用户,然后在发送消息的时候,将uuid传递过去,然后在SharedWorker中判断,如果是私聊,就只发送给指定的用户。

这一块就留给感兴趣的同学自己实现了。

SharedWorker 的关闭

上面我们实现了一个简单的聊天室,但是我们发现,当我们关闭页面的时候,SharedWorker并没有关闭,这是因为SharedWorker是一个长连接,当我们关闭页面的时候,SharedWorker并没有关闭,所以我们需要在页面关闭的时候,手动关闭SharedWorker

// 关闭页面的时候,关闭 SharedWorker
window.addEventListener('beforeunload', () => {sharedWorker.port.postMessage({type: 'close',uuid: uuid});
});

// SharedWorker.js
/** 省略其他代码 **/
onconnect = (e) => {const port = e.ports[0];port.onmessage = (e) => {if (e.data.type === 'close') {const index = ports.indexOf(port);ports.splice(index, 1);return;}};
}; 

上面的代码中,我们在页面关闭的时候,发送一个close的消息,然后在SharedWorker中,将portports中移除,这样就可以关闭SharedWorker了。

SharedWorker 的生命周期

SharedWorker的生命周期没有那么复杂,不像ServiceWorker有那么多的过程,它只有一条规则,就是当所有创建SharedWorker的页面关闭之后,那么SharedWorker的生命就走到了尽头,否则它就会一直常驻。

SharedWorker 还可以做什么?

聊天室只是一个小玩具,我们还可以做很多事情,我们最常见的就是跨页面登录,例如GitHub,我们在一个页面登录,然后在其他页面就可以获取到登录信息,这就是SharedWorker的一个应用场景。

还有一个应用场景就是,我们可以在一个页面中,监听一个WebSocket,然后在其他页面中,发送消息,这样就可以实现跨页面的WebSocket

其实任何跨页面的通信,都可以使用SharedWorker来实现,但是SharedWorker的缺点也很明显,就是不支持IE,所以如果你的项目需要兼容IE,那么就不要使用SharedWorker了。

优点很明确,就是可以跨页面通信,缺点也很明显,就是浏览器兼容确实有点捉急。

总结

本文主要介绍了SharedWorker的基本使用,并使用SharedWorker实现了一个简单的聊天室。

SharedWorker的使用还是比较简单的,不同于Worker那么单纯,SharedWorker是一个长连接,所以我们可以在多个页面中,共享一个SharedWorker,这样就可以实现跨页面通信了。

不同于ServiceWorker有那么多的限制,SharedWorker除了兼容性其实功能非常强大,且在实际项目中的应用场景也更加广泛。

最后

为大家准备了一个前端资料包。包含54本,2.57G的前端相关电子书,《前端面试宝典(附答案和解析)》,难点、重点知识视频教程(全套)。



有需要的小伙伴,可以点击下方卡片领取,无偿分享

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/185963.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

i.MX8MM开发板音视频开发-音频编码

我们举个例子&#xff0c;以 CD 音质来说&#xff0c;量化格式是 2 字节&#xff0c;采样率是 44100&#xff0c;声道数是 2&#xff0c;这些信息就描述 了 CD 的音质。对于声音信息&#xff0c;我们还可以用数据比特率来描述音频数据单位时间内的容量大小。那么 CD 的 数 据 采…

【Java AWT 图形界面编程】Frame 窗口中进行自定义布局 ( AWT 中常用的布局容器 )

文章目录一、Frame 窗口中进行自定义布局二、AWT 中常用的布局容器一、Frame 窗口中进行自定义布局 在 【Java AWT 图形界面编程】LayoutManager 布局管理器总结 ( FlowLayout 布局 | BorderLayout 布局 | BoxLayout 布局 ) 介绍了常用的布局 ; 使用布局的好处是 布局内的子组…

【服务器数据恢复】ZFS文件系统下RAIDZ的数据恢复案例

服务器数据恢复环境&#xff1a; ORACLE Sun ZFS Storage&#xff1b; 32块磁盘分为4组&#xff0c;每组8块硬盘&#xff0c;热备盘全部启用。 ZFS文件系统&#xff0c;Windows操作系统。 服务器故障&分析&#xff1a; 设备在正常工作时候突然崩溃&#xff0c;经过检查排除…

机器学习笔记之深度玻尔兹曼机(三)预训练思路整理

机器学习笔记之深度玻尔兹曼机——预训练思路整理引言回顾&#xff1a;受限玻尔兹曼机的叠加逻辑回顾&#xff1a;受限玻尔兹曼机叠加过程中的计算方式关于计算过程的优化引言 上一节介绍了受限玻尔兹曼机叠加的逻辑&#xff0c;以及叠加过程中出现的Double Counting\text{Dou…

理解 TypeScript 背后的结构化类型系统

前言 你能说清楚类型、类型系统、类型检查这三个的区别吗&#xff1f;在理解TypeScript的结构化类型系统之前&#xff0c;我们首先要搞清楚这三个概念和它们之间的关系 类型&#xff1a;即对变量的访问限制与赋值限制。如 TypeScript 中的原始类型、对象类型、函数类型和字面…

python代码实现批量yunfile文件下载

建议下载文件大小不要超过1M吧,超过的话,把等待下载时间加大点 —>说明: 使用python2.7+selenium+chrome v49+百度ocr识别,基本上pip install 相关的程序,源码就可以直接运行了,exe文件也编译了,去别的电脑运行感觉有各种问题,大家自己用源码跑吧,最新chrome版本的…

如何实现报表集成?(一)

报表需求在每个企业都是“刚需”&#xff0c;而报表的应用又是其中的关键之一&#xff0c;并不是说报表开发出来就万事大吉了&#xff0c;怎么用、怎么用得好&#xff0c;也是用户非常关注的。在这个话题中&#xff0c;报表的集成是个绕不过去的坎&#xff0c;如何通过集成&…

机器学习之参数学习

下述内容为课程小结 定义 参数估计的方法包括经验风险最小化、结构风险最小化、最大似然估计、最大后验估计。 参数估计用于学习模型参数&#xff0c;以达到最优的目的&#xff0c;如线性回归的模型参数 经验风险最小化 对于输入的待处理数据格式为(x&#xff0c;y){(x&…

Plant Simulation热力图工具V2.1全新发布

在做AGV路径规划或人员路径规划时&#xff0c;如果配套热力图&#xff0c;是可以非常方便的分析出相应位置的热点情况&#xff0c;决策人员可以更加方便的确定方案修改思路&#xff0c;比如下图可以非常清晰地看到AGV的停顿位置和路口的使用情况。较早之前&#xff0c;波哥开发…

Vue3+Vite+Element-Plus实现CRUD常见表单项目

效果有 查询&#xff0c;增加&#xff0c;表格&#xff0c;删除&#xff0c;编辑 其实CRUD&#xff0c;就是一个管理项目最常见的功能 C增加 (Create) R读取 (Read) U更新 (Update) D删除 (Delete) 一、创建项目 vue3用vite创建项目 1 对应路径cmd 输入 npm create vitelates…

C 语言零基础入门教程(十八)

C 输入 & 输出 当我们提到输入时&#xff0c;这意味着要向程序填充一些数据。输入可以是以文件的形式或从命令行中进行。C 语言提供了一系列内置的函数来读取给定的输入&#xff0c;并根据需要填充到程序中。 当我们提到输出时&#xff0c;这意味着要在屏幕上、打印机上或…

SSM框架整合(Spring+SpringMVC+MyBatis)

一、创建MAVEN工程 二、导入pom依赖 <dependency><groupId>com.mchange</groupId><artifactId>c3p0</artifactId><version>0.9.5.2</version></dependency><!-- https://mvnrepository.com/artifact/commons-logging/comm…

项目错误排查

项目运行不起来&#xff0c;先观察表现&#xff0c;就像中医所讲的望。复现&#xff0c;了解触发问题的时机和过程。在哪个步骤&#xff0c;哪个接口出了问题。闻问切浏览器f12&#xff0c;根据请求参数和响应码判断问题出在前端还是后端。查看错误日志&#xff0c;一般写的还是…

LeetCode011之盛最多水的容器(相关话题:双指针,逻辑分析)

题目描述 给定一个长度为 n 的整数数组 height 。有 n 条垂线&#xff0c;第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。 找出其中的两条线&#xff0c;使得它们与 x 轴共同构成的容器可以容纳最多的水。 返回容器可以储存的最大水量。 说明&#xff1a;你不能倾斜容…

小程序 --- 收货地址 --- 定位功能

一、整体功能如下图二、功能点定位选择自己的位置手动选择省市县后输入详细地址关键词搜索地址&#xff0c;然后进行选择三、如何实现定位采用小程序API wx.chooseLocation 调出地图选择位置 API wx.chooseLocation 详细说明根据地址解析出省市县以及详细地址及经纬度代码实现c…

【Linux】-- 进程概念

目录 一、进程概念 二、PCB 1.什么是PCB 2.什么是task_struct 3.task_struct包含内容 三、task_struct内容详解 1.查看进程 &#xff08;1&#xff09;通过系统目录查看 &#xff08;2&#xff09;通过ps命令查看 &#xff08;3&#xff09;通过top命令查看 &…

Mysql自定义变量在递归遍历中的妙用

借着在解决一个递归查询父目录的问题&#xff0c;学习了一下mysql变量的用法&#xff0c;在某些场景下这种解法还是比较有特效的&#xff0c;下面具体来聊一下场景&#xff0c;同时也会详细分析下mysql自定义变量的用法 场景&#xff1a; 获取从树的根节点到叶子节点的全路径&…

图解基于UDS的Flash BootLoader

图解基于UDS的Flash BootLoader一、为什么要搞Bootloader&#xff1f;为什么要基于UDS搞Bootloader二、Bootloader应支持的UDS服务三、Bootloader——三段式(1) 预编程阶段(2) 主编程阶段(3)后编程状态四、BootLoader的启动顺序与转换流程五、问题点疑问点 Q:图中的烧写顺序是…

点云 3D 目标检测 - RangeDet(ICCV 2021)

点云 3D 目标检测 - RangeDet&#xff08;ICCV 2021&#xff09;摘要1. 引言2. 相关工作3. 距离视图表示的回顾4. 方法4.1 距离条件金字塔4.2 元核卷积4.3 加权非最大抑制4.4 距离视图中的数据增强4.5 体系结构5. 实验5.1 元核卷积的研究5.2 距离条件金字塔的研究5.3 加权非最大…

基于android的有声听书系统

需求信息&#xff1a; 1&#xff1a;注册登录&#xff1a;未注册用户首先进行账号注册&#xff0c;注册成功后进行登录&#xff0c;已注册用户直接输入账号密码进行登录&#xff0c;登录成功后进入主页面。 2&#xff1a;主页面&#xff1a;通过左右滑动可以实现对推荐界面、订…