shared worker实现多页面通信

4,583 阅读3分钟

shared worker实现多页面通信

背景

中后台项目场景中,业务人员经常为了操作方便,同一个后台系统浏览器打开多个tab页面来操作,当打开多个浏览器tab页面时,例如全局通知框、或者某定制的消息通知等业务组件,在某个页面关掉时,其他的tab页面之下是不会同步关掉的,这就影响到了业务人员使用系统的效率,此时我们需要同时关闭掉其他页面的通知框,避免重复显示,影响效率和体验。

如图:(此类全局通知框或者特殊设计的通知框等不自动关闭) image.png

解决方案:

有localstorage、sharedworker、websocket等方案,最近组内开始有用到worker了,对于这个没用使用过的html5新特性,学习了一下,下面介绍使用sharedworker的解决方案。

worker 介绍:

Web Worker 是 HTML5 标准的一部分,这一规范定义了一套 API,它允许一段 JavaScript 程序运行在主线程之外的另外一个线程中。

这在很大程度上利用了现在不断升级的电脑计算能力:能够在同一时间平行处理两个任务,而不影响主线程ui渲染。

worker基本用法

主线程采用new命令,调用Worker()构造函数,新建一个 Worker 线程。

const worker = new Worker('work.js');

主线程调用worker.postMessage()方法,向 Worker 发消息。

worker.postMessage('Hello World');

主线程通过worker.onmessage指定监听函数,接收子线程发回来的消息。

worker.onmessage = function (event) {
  console.log('Received message ' + event.data);
  doSomething();
}

Worker 线程内部需要有一个监听函数,监听message事件。

self.addEventListener('message', function (e) {
  self.postMessage('You said: ' + e.data);
}, false);

通过self.postMessage给主线程发送消息。

shared worker 介绍:

当前我们用到的是shared worker,shared worker是webworker的一种,由同源的所有页面共享。 shared worker普通 Worker 区别:

1、 同一个js脚本会创建一个 sharedWorker,其他页面再使用同样的脚本创建sharedWorker,会复用已创建的 worker,这个worker由几个页面共享,顾名思义叫shared worker。

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

用法:

1.调试方法:

如图,打开chrome://inspect 选择shared workers,会打开一个devtool,worker文件中的网络请求,打印都在这个里边进行查看

image.png

image.png image.png

2.spa项目中应用

安装worker-loader

module.exports = { 
chainWebpack: config => 
{ 
config.module 
.rule('worker') 
.test(/\.worker\.js$/) 
.use('worker') 
.loader('worker-loader') 
.tap(() => ({ worker: 'SharedWorker' }))
} 
}

2.向多个页面发布消息

注册worker时,将每个页面的worker缓存起来,向所有页面广播消息时,遍历ports,逐个去发消息,当页面关闭时,广播关闭worker的消息,清除缓存

// 页面 window.onbeforeunload = () => { myWorker.port.postMessage('CLOSE'); };

// 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);
                                                                                              if (e.data === 'CLOSE'){ 
                                                                                              const index = ports.findIndex(p => p === port); 
                                                                                              portPool.splice(index, 1); }
	}
}

function boardcast(message) {
	portPool.forEach(port => {
		port.portMessage(port);
	})
}

更多应用场景

1.webworker单开线程跑计算量大、耗时的任务,不影响主线程ui渲染

2.页面之间登录状态通知(如:github的登录状态)

3.后台spa项目中,多个浏览器tab页面数据通信 等。。。

参考:

developer.mozilla.org/zh-CN/docs/…

www.ruanyifeng.com/blog/2018/0…

www.cnblogs.com/imgss/p/146…