是这样的,今天玩github,先是在没有登录浏览了一些页面,然后在某一页面进行了登录。这时再切换的其他页面时就看到了下面的提示:

那么这是怎么做到的呢?我们可以想到,一种办法是 localStorage,在某一个页面登录时,修改localStorage 状态,其他页面在显示的时候,读取最新的状态,然后显示提示:

// 登录的页面
localStorage.setItem('login', true); // 其他页面
document.addEventListener("visibilitychange", function() {
if (localStorage.setItem('login') === 'true') {
alert('你已登录,请刷新页面');
}
}

然而,github并没有这么做,localStorage里也找不到相关的字段,一番查找之后,发现他们是用 sharedWorker 实现的。那我们就来了解下sharedworker

什么是sharedWorker

sharedWorker 顾名思义,是 worker 的一种,可以由所有同源的页面共享。同Worker的api一样,传入js的url,就可以注册一个 sharedWorker 实例:

let myWorker = new SharedWorker('worker.js');

但是与普通 Worker 不同的是:

1 同一个js url 只会创建一个 sharedWorker,其他页面再使用同样的url创建sharedWorker,会复用已创建的 worker,这个worker由那几个页面共享。

2 sharedWorker通过port来发送和接收消息

接下来,我们看一下具体是 worker 和页面之间是如何发送和接收消息的。

messagePort

假设我们有两个js,一个是跑在页面里的 page.js,另一个是跑在 worker里的 worker.js。那么我们要在 page.js 里注册一个 sharedWorker,代码如下:

// page.js
let myWorker = new SharedWorker('worker.js');
// page通过worker port发送消息
myWorker.port.postMessage('哼哼');
// page通过worker port接收消息
myWorker.port.onmessage = (e) => console.log(e.data); // worker.js
onconnect= function(e) {
const port = e.ports[0];
port.postMessage('哈嘿');
port.onmessage = (e) => {
console.log(e.data);
}
}

调试sharedWorker

在上面的例子中,我们在worker中使用了console.log来打印来自页面的message,那么到哪里可以看到打印的log呢?我们可以在浏览器地址栏里面输入 `chrome://inspect

,然后在侧边栏选中shared workers了,就可以看到浏览器,目前在运行的所有worker。点击inspect会打开一个开发者工具,然后就可以看到输出的log了。

这里我们看到我们的worker名字是untitled,那是因为sharedworker 构造函数还支持传入第二个参数作为名字:

let myWorker = new SharedWorker('worker.js', 'awesome worker');

多页面发布消息

回到文章一开始的例子,我们前面实现了页面和worker之间的通信,那么该如何让worker向多个页面发送消息呢?一个思路就是我们把port缓存起来,作为一个port pool,这样当我们需要向所有页面广播消息的时候,就可以遍历port,然后发送消息:

// worker js
const portPool = [];
onconnect= function(e) {
const port = e.ports[0];
// 在connect时将 port添加到 portPool中
portPool.push(port);
port.postMessage('哈嘿');
port.onmessage = (e) => {
console.log(e.data);
}
} function boardcast(message) {
portPool.forEach(port => {
port.portMessage(port);
})
}

这样我们就基本实现了向多个页面广播消息的功能。

清除无效的port

上面的实现中有一个问题,就是在页面关闭后,workerPool中的port并不会自动清除,造成内存的白白浪费。我们可以在页面关闭前通知shared worker页面将要关闭,然后让worker将无效的 messagePort 从 portPool 中移除。

// 页面
window.onbeforeunload = () => {
myWorker.port.postMessage('TO BE CLOSED');
}; // worker.js
const portPool = [];
onconnect = function(e) {
var port = e.ports[0];
portPool.push(port);
port.onmessage = function(e) {
console.log(e);
if (e.data === 'TO BE CLOSED') {
const index = ports.findIndex(p => p === port);
portPool.splice(index, 1);
}
var workerResult = 'Result: ' + (e.data[0] * e.data[1]);
port.postMessage(workerResult);
}
} function boardcast(message) {
portPool.forEach(port => {
port.portMessage(port);
})
}

这样,我们就实现了一个简单的多页面广播的sharedWorker。我们可以用它来广播一下时间:

setInterval(() => boardcast(Date.now()), 1000);

参考

https://developer.mozilla.org/en-US/docs/Web/API/SharedWorker/SharedWorker

https://github.com/mdn/simple-shared-worker

sharedWorker 实现多页面通信的更多相关文章

  1. js之iframe子页面与父页面通信

    iframe子页面与父页面通信根据iframe中src属性是同域链接还是跨域链接,通信方式也不同. 一.同域下父子页面的通信 父页面parent.html <html> <head& ...

  2. 学习安卓开发[1] - 程序结构、Activity生命周期及页面通信

    一.程序结构 Android原生应用采用了MVC的架构设计模式,因此可以将一个Android APP中的对象归为Model.View或Controller中的一种. 具体到某个实际的APP结构中,它一 ...

  3. [转]html5: postMessage解决跨域和跨页面通信的问题

    [转]html5: postMessage解决跨域和跨页面通信的问题 平时做web开发的时候关于消息传递,除了客户端与服务器传值,还有几个经常会遇到的问题: 多窗口之间消息传递(newWin = wi ...

  4. iframe之父子页面通信

    iframe之父子页面通信 1.获取 子页面 的 window 对象  在父页面中,存在如下两个对象 window.frames document.iframeElement.contentWindo ...

  5. window.frames && iframe 跨页面通信

    1.定义 frames[]是窗口中所有命名的框架组成的数组.这个数组的每个元素都是一个Window对象,对应于窗口中的一个框架. 2.用法 假设iframe 是一个以存在的 iframe 的 ID 和 ...

  6. js之iframe父、子页面通信

    注意事项 一 . 页面加载顺序:一般先加载完父页面才会去加载子页面,所以:必须要确保在iframe加载完成后再进行操作,如果iframe还未加载完成就开始调用里面的方法或变量,会产生错误.判断ifra ...

  7. 渐进式web应用开发---Service Worker 与页面通信(七)

    _ 阅读目录 一:页面窗口向 service worker 通信 二:service worker 向所有打开的窗口页面通信 三:service worker 向特定的窗口通信 四:学习 Messag ...

  8. 微信小程序页面通信

    目录 微信小程序页面通信 方式一:通过URL 方式二:通过全局变量 方式三:通过本地存储 方式四:通过路由栈 微信小程序页面通信 方式一:通过URL // A 页面 wx.navigateTo({ u ...

  9. iframe子页面与父页面通信

    同域下父子页面的通信 父页面: <!DOCTYPE html> <html> <head lang="en"> <meta charset ...

随机推荐

  1. 微信小程序(二)-语法学习

    语法学习 一 模板语法 https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxml/ 1.数据代码 // pages/bl ...

  2. 树的遍历c/c++

    先序遍历(递归) 1 void preOrderTraverase(TreeNode * r) 2 { 3 if(r) 4 { 5 printf("%d\t",r->_dat ...

  3. Bootstrap下拉菜单、按钮式下拉菜单

    1. 概述 下拉菜单使用频率也是比较高的,比较常见的使用场景是在导航菜单栏,某个主菜单含有下拉的子菜单. Bootstrap为下拉菜单提供了两种实现方式,即普通的下拉菜单还有按钮式的下拉菜单.我们先看 ...

  4. Kubernetes 实战 —— 01. Kubernetes 介绍

    简介 P2 Kubernetes 能自动调度.配置.监管和故障处理,使开发者可以自主部署应用,并且控制部署的频率,完全脱离运维团队的帮助. Kubernetes 同时能让运维团队监控整个系统,并且在硬 ...

  5. Markdown(3)Typora快捷键

    Typora(3)快捷键 一.快捷键列表 操作内容 快捷键 助记词 1. 标题 Ctrl + 数字 1-6 2. 段落 Ctrl + 数字 0 3. 粗体 Ctrl + B (B) Bold 4. 斜 ...

  6. Go benchmark 一清二楚

    前言 基准测试(benchmark)是 go testing 库提供的,用来度量程序性能,算法优劣的利器. 在日常生活中,我们使用速度 m/s(单位时间内物体移动的距离)大小来衡量一辆跑车的性能,同理 ...

  7. 身份认证:JSON Web Token

    JSON Web Token(JWT)是一种基于JSON的开放标准((RFC 7519),也是目前最流行的跨域认证解决方案. 传统的 cookie 认证方式看起来遵守了 REST 架构的无状态要求,但 ...

  8. mongodb 聚合(Map-Reduce)

    介绍 Map-reduce 是一种数据处理范式,用于将大量数据压缩为有用的聚合结果.对于 map-reduce 操作,MongoDB 提供MapReduce数据库命令. MongoDB中的MapRed ...

  9. MySQL基础知识:创建MySQL数据库和表

    虚构一个微型在线书店的数据库和数据,作为后续MySQL脚本的执行源,方便后续MySQL和SQL的练习. 在虚构这个库的过程中,主要涉及的是如何使用命令行管理 MySQL数据库对象:数据库.表.索引.外 ...

  10. redis安装以及使用

    一.安装 1.源码安装 1.下载redis源码 $ wget http://download.redis.io/releases/redis-4.0.10.tar.gz 2.解压缩 $ tar -zx ...