浏览器中的跨页面通信(二):SharedWorker

介绍

SharedWorker 接口代表一种特定类型的 worker,可以从几个浏览上下文中访问,例如几个窗口、iframe 或其他 worker。它们实现一个不同于普通 worker 的接口,具有不同的全局作用域SharedWorkerGlobalScope 。

备注:  如果要使 SharedWorker 连接到多个不同的页面,这些页面必须是同源的(相同的协议、host 以及端口)。

又称共享线程。之所以可以在多个浏览器页签中共享信息,是因为在页签的html中创建SharedWorker时,如果第一个参数相同,则视为同一线程,所以只要保证创建时的第一个参数相同,就可以在多页签间共享信息。

方法介绍

  • 构造函数 new Worker(arg) :参数表示你的线程要执行的代码所在的js文件,例如‘myworker.js’,构造函数当然是返回一个Worker类的实例

    1
    var myWorker = new SharedWorker("worker.js");
  • worker.postMessage(message):这个方法表示从主线程向子线程发送消息或者子线程向主线程发送消息,message一般是一个字符串,也可以将一个js对象转成字符串发过去

  • worker上还有一个onmessage事件,当有人向这个worker实例发送消息时,该事件被触发,我们可以从他的事件对象的data属性中获得post过来的值

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    first.onchange = function () {
    myWorker.port.postMessage([first.value, second.value]);
    console.log("Message posted to worker");
    };

    second.onchange = function () {
    myWorker.port.postMessage([first.value, second.value]);
    console.log("Message posted to worker");
    };

    myWorker.port.onmessage = function (e) {
    result1.textContent = e.data;
    console.log("Message received from worker");
    };

      可以看到Woker类的API是相当简洁的,只有两个最常用的方法,一个事件,下面我们来通过实际的例子看看。

    示例

调试:

地址栏chrome://inspect,点击shared workers,点击inspect

构建worker脚本

1
2
3
4
5
6
7
8
9
10
let pool = [];
onconnect = function (e) {
let port = e.ports[0];
pool.push(port);
port.onmessage = function (e) {
pool.forEach((v) => {
v != port && v.postMessage(e.data);
});
};
};

构建数据共享通道

1
2
3
4
5
6
7
8
9
10
11
12
export class SharedDataChannel {
constructor(url, cb) {
this.sharedWorker = new SharedWorker(url);
this.sharedWorker.port.onmessage = (e) => {
cb && cb(e.data);
};
}

post(data) {
this.sharedWorker.port.postMessage(data);
}
}

在多个页面进行初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<script type="module">
import { SharedDataChannel } from "./index.js";
const channel = new SharedDataChannel("./worker.js", (res) => {
document.querySelector("#content").innerHTML += `${res} </br>`;
});
document.querySelector("#send").addEventListener("click", () => {
channel.post("登录页面发送的数据");
});
</script>

<body>
登录页面

<button id="send">发送</button>
<p id="content"></p>
</body>

调试

chrome://inspect/#workers 导航到 chrome://inspect/#workers 并找到共享的 worker,然后单击”inspect”,然后你可以拉起 SharedWorker 的控制台。

效果

页面关闭清除Shared Worker
.在页面关闭后,workerPool中的port并不会自动清除,造成内存的占用
.通过监听页面关闭,通知worker关闭
.通过框架的生命周期
.通过浏览器自带的生命周期window.onbeforeunload

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 关闭页面的时候,关闭 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;
}
};
};

兼容性