Skip to content

SharedWorker

SharedWorker支持多个页面访问同一个worker,并且有同源限制,它接收一个脚本URL以及可选的name。

初始化

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

通信

与SharedWorker的通信是通过显式的 MessagePort 对象完成的。 SharedWorker() 构造函数返回的对象在其端口属性上保存对端口的引用。

js
worker.port.onmessage = function(event) {
  // todo 
};
worker.port.postMessage('some message');
worker.port.postMessage({ foo: 'structured', bar: ['data', 'also', 'possible'] });

在service.js内部使用onconnect监听连接事件

js
onconnect = function(event) {
  var newPort = event.source;
  // set up a listener
  newPort.onmessage = function(event) {
    // todo 
  };
  // send a message back to the port
  newPort.postMessage('ready!'); // can also send structured data, of course
};

使用场景

INFO

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

点击inspect则会自动弹出一个控制台,控制台上输出的是worker中打印的文本

img.png

在两个同源页面之间通信

需要将port实例存入map中,方便存取

js
// worker.js
import { md5 } from 'js-md5';

const ports = new Map();

self.onconnect = (event) => {
  const port = event.source;

  // set up a listener
  port.onmessage = function(event) {
    const { type, data } = event.data;
    const rawKey = md5(data.key);
    switch (type) {
      case 'init':
        if (!ports.get(rawKey)) {
          ports.set(rawKey, port);
        }
        break;
      case 'unmounted':
        ports.delete(rawKey);
        break;
      case 'broadcast':
        const channel = ports.get(rawKey);

        if (channel) {
          channel.postMessage(data.data);
        }
        break;
    }
  };

  // send a message back to the port
  port.postMessage('ready!'); // can also send
};

在每个页面中首先执行postMessage,触发worker保存key和对应的MessagePort实例

js
// index.vue
const worker = new SharedWorker(new URL('./worker.js', import.meta.url), { type: 'module' });

worker.port.onmessage = (event) => {
  console.log('>>>event:', event);
};
worker.port.start();

worker.port.postMessage({ type: 'init', data: { key: 'index.vue' } });

const count = ref(0);

const { pause } = useRafFn(
  () => {
    worker.port.postMessage({ type: 'broadcast', data: { key: 'index2.vue', data: count.value++ } });
  },
  {
    immediate: true,
    fpsLimit: 1,
  },
);
js
// index2.vue
const worker = new SharedWorker(new URL('./worker.js', import.meta.url), { type: 'module' });
worker.port.onmessage = (event) => {
  console.log('>>>event:', event);
};

worker.port.postMessage({ type: 'init', data: { key: 'index2.vue' } });
onUnmounted(() => {
  worker.port.postMessage({ type: 'unmounted', data: { key: 'index2.vue' } });
});