我们需要了解的Web Workers

【阅读完这篇文章我们就可以更好的理解线程/进程】

JavaScript最初设计为单线程运行在浏览器中,主要是为了避免多个线程同时操作DOM导致的渲染冲突问题‌。这种设计确保了页面渲染的一致性和安全性,简化了编程模型‌

。然而,随着前端技术的发展,JavaScript的应用场景已远超简单的页面交互,当遇到图像处理、视频解码等需要大量计算的场景时,单线程的局限性就显现出来:主线程会被长时间阻塞,导致页面卡顿,严重影响用户体验‌。

为了解决单线程的瓶颈,HTML5引入了Web Worker标准‌

。Web Worker允许JavaScript脚本创建多个子线程,这些子线程可以执行耗时的计算任务,从而解放主线程,避免阻塞UI渲染和事件响应‌。需要注意的是,子线程完全受主线程控制,且无法直接操作DOM。通过将任务拆分为多个小任务(每个任务执行时间控制在50毫秒以内),并使用setTimeout或queueMicrotask等机制调度,可以有效避免主线程被长时间占用‌。

进程与线程的区别

在介绍进程与线程的概念前,我们先来看个进程与线程之间关系形象的比喻:

如上图所示,进程是一个工厂,它有独立的资源,线程是工厂中的工人,多个工人协作完成任务,工人之间共享工厂内的资源,比如工厂内的食堂或餐厅。此外,工厂(进程)与工厂(进程)之间是相互独立的。为了让大家能够更直观地理解进程与线程的区别,我们继续来看张图:

由上图可知,操作系统会为每个进程分配独立的内存空间,一个进程由一个或多个线程组成,同个进程下的各个线程之间共享程序的内存空间。相信通过前面两张图,小伙伴们对进程和线程之间的区别已经有了一定的了解,那么实际情况是不是这样呢?这里我们打开 macOS 操作系统下的活动监视器,来看一下写作本文时所有进程的状态:

(这里大家可以打开windows的进程,也能看见不同的进程有多条线程在进行中。)

通过上图可知,我们常用的软件,比如微信和搜狗输入法都是一个独立的进程,拥有不同的 PID(进程 ID),而且图中的每个进程都含有多个线程,以微信进程为例,它就含有 「36」 个线程。那么什么是进程和线程呢?下面我们来介绍进程和线程的概念。

进程和线程是操作系统中两个核心概念,它们在资源分配、执行方式和应用场景上有着显著区别。‌

基本定义

‌进程‌ 是操作系统进行资源分配和调度的基本单位,拥有独立的地址空间和系统资源。‌可以理解为程序的一次执行实例。
‌线程‌是进程内部的一个执行单元,是CPU调度的基本单位,共享所属进程的地址空间和资源。‌一个进程可以包含多个线程。

核心区别

1. 资源分配‌ :进程是资源分配的基本单位,拥有独立内存空间;线程共享进程资源,不拥有独立资源。‌
2. 独立性‌ :进程是独立运行的单位;线程不能独立执行,必须依赖于进程。‌
3. 切换开销‌ :进程切换需要保存和恢复整个地址空间,开销较大(1-5微秒);线程切换只需保存寄存器状态,开销较小(0.1-0.3微秒)。‌
4. 健壮性‌:进程间通过IPC机制通信,错误隔离性好;线程间直接共享内存,一个线程的非法操作可能导致整个进程崩溃。‌

单线程与多线程

如果一个进程只有一个线程,我们称之为单线程。单线程在程序执行时,所走的程序路径按照连续顺序排下来,前面的必须处理好,后面的才会执行。单线程处理的优点:同步应用程序的开发比较容易,但由于需要在上一个任务完成后才能开始新的任务,所以其效率通常比多线程应用程序低。

如果完成同步任务所用的时间比预计时间长,应用程序可能会不响应。针对这个问题,我们可以考虑使用多线程,即在进程中使用多个线程,这样就可以处理多个任务。

对于 Web 开发者熟悉的 JavaScript 来说,它运行在浏览器中,是单线程的,每个窗口一个 JavaScript 线程,既然是单线程的,在某个特定的时刻,只有特定的代码能够被执行,其它的代码会被阻塞。

"JS 中其实是没有线程概念的,所谓的单线程也只是相对于多线程而言。JS 的设计初衷就没有考虑这些,针对 JS

这种不具备并行任务处理的特性,我们称之为 "单线程"。 ------ 来自知乎 "如何证明 JavaScript 是单线程的?"

浏览器内核

其实在浏览器内核(渲染进程)中除了 JavaScript 引擎线程之外,还含有 GUI 渲染线程、事件触发线程、定时触发器线程等。因此对于浏览器的渲染进程来说,它是多线程的。多个协同工作的线程,共同完成页面的渲染和交互任务。

主要线程

  1. GUI渲染线程‌
    GUI 渲染线程负责渲染浏览器界面,解析 HTML,CSS,构建 DOM 树和 RenderObject 树,布局和绘制等。当界面需要重绘(Repaint)或由于某种操作引发回流(Reflow)时,该线程就会执行。
  2. JavaScript引擎线程‌
    JavaScript 引擎线程负责解析 JavaScript 脚本并运行相关代码。 JavaScript 引擎一直等待着任务队列中任务的到来,然后进行处理,一个Tab页(Renderer 进程)中无论什么时候都只有一个 JavaScript 线程在运行 JavaScript 程序。
    需要注意的是,GUI 渲染线程与 JavaScript 引擎线程是互斥的,所以如果 JavaScript 执行的时间过长,这样就会造成页面的渲染不连贯,导致页面渲染被阻塞。
    ‌3. 事件触发线程‌
    当一个事件被触发时该线程会把事件添加到待处理队列的队尾,等待 JavaScript 引擎的处理。这些事件可以是当前执行的代码块如定时任务、也可来自浏览器内核的其他线程如鼠标点击、AJAX 异步请求等,但由于 JavaScript 引擎是单线程的,所有这些事件都得排队等待 JavaScript 引擎处理。
    ‌4. 定时器触发线程‌
    浏览器定时计数器并不是由 JavaScript 引擎计数的,这是因为 JavaScript 引擎是单线程的,如果处于阻塞线程状态就会影响记计时的准确,所以通过单独线程来计时并触发定时是更为合理的方案。我们日常开发中常用的 setInterval 和 setTimeout 就在该线程中。
  3. 异步HTTP请求线程‌
    在 XMLHttpRequest 在连接后是通过浏览器新开一个线程请求, 将检测到状态变更时,如果设置有回调函数,异步线程就产生状态变更事件放到 JavaScript 引擎的处理队列中等待处理。
    前面我们已经知道了,由于 JavaScript 引擎与 GUI 渲染线程是互斥的,如果 JavaScript 引擎执行了一些计算密集型或高延迟的任务,那么会导致 GUI 渲染线程被阻塞或拖慢。那么如何解决这个问题呢?嘿嘿,当然是使用本文的主角 ------ Web Workers。
    ‌6. 合成线程‌
    优化图层合成,提升渲染性能‌。

Web Worker 是什么

Web Worker 作为 HTML5 标准的关键特性,其核心在于通过一套 API 机制,在 JavaScript 主线程之外创建独立的 Worker 线程。这种设计突破了传统单线程模型的限制,使开发者能够利用 JavaScript 实现多线程操作。

由于 Worker 线程与主线程相互独立并行运行,二者不会相互阻塞。当面对大规模计算任务时,开发者可将计算逻辑转移至 Worker 线程处理。待计算完成后,Worker 线程再将结果返回主线程。这种分工模式使主线程得以专注于业务逻辑处理,无需耗费大量时间执行复杂运算,从而有效减少阻塞现象,显著提升运行效率,最终带来更流畅的页面交互体验和更优质的用户感受。

(图片来源:https://thecodersblog.com/web-worker-and-implementation/)

Web Workers 的限制与能力

通常情况下,你可以在 Worker 线程中运行任意的代码,但注意存在一些例外情况,比如:「直接在 worker 线程中操纵 DOM 元素,或使用 window 对象中的某些方法和属性 。」 大部分 window 对象的方法和属性是可以使用的,包括 WebSockets,以及诸如 IndexedDB 和 FireFox OS 中独有的 Data Store API 这一类数据存储机制。

下面我们以 Chrome 和 Opera 所使用的 Blink 渲染引擎为例,介绍该渲染引擎下 Web Worker 中所支持的常用 APIs:

Cache: Cache 接口为缓存的 Request / Response 对象对提供存储机制,例如,作为ServiceWorker 生命周期的一部分。
CustomEvent: 用于创建自定义事件。
Fetch: Fetch API 提供了一个获取资源的接口(包括跨域请求)。任何使用过 XMLHttpRequest 的人都能轻松上手,而且新的 API 提供了更强大和灵活的功能集。Promise: Promise 对象代表了未来将要发生的事件,用来传递异步操作的消息。FileReader:FileReader 对象允许 Web 应用程序异步读取存储在用户计算机上的文件(或原始数据缓冲区)的内容,使用 File 或 Blob 对象指定要读取的文件或数据。
IndexedDB: IndexedDB 是一种底层 API,用于客户端存储大量结构化数据,包括文件/二进制大型对象(blobs)。
WebSocket: WebSocket 对象提供了用于创建和管理 WebSocket 连接,以及可以通过该连接发送和接收数据的 API。
XMLHttpRequest: XMLHttpRequest(XHR)对象用于与服务器交互。通过 XMLHttpRequest 可以在不刷新页面的情况下请求特定 URL,获取数据。这允许网页在不影响用户操作的情况下,更新页面的局部内容。

Web Worker 使用

创建 worker 只需要通过 new 调用 Worker() 构造函数即可,它接收两个参数。

复制代码
const worker = new Worker(path, options);
参数 说明
path 有效的js脚本的地址,必须遵守同源策略。无效的js地址或者违反同源策略,会抛出SECURITY_ERR 类型错误
options.type 可选,用以指定 worker 类型。该值可以是 classic 或 module。 如未指定,将使用默认值 classic
options.credentials 可选,用以指定 worker 凭证。该值可以是 omit, same-origin,或 include。如果未指定,或者 type 是 classic,将使用默认值 omit (不要求凭证)
options.name 可选,在 DedicatedWorkerGlobalScope 的情况下,用来表示 worker 的 scope 的一个 DOMString 值,主要用于调试目的。

js 主线程与 worker 线程数据传递

复制代码
主线程与 worker 线程都是通过 postMessage 方法来发送消息,以及监听 message 事件来接收消息。如下所示:
// main.js(主线程)

const myWorker = new Worker('/worker.js'); // 创建worker

myWorker.addEventListener('message', e => { // 接收消息
    console.log(e.data); // Greeting from Worker.js,worker线程发送的消息
});

// 这种写法也可以
// myWorker.onmessage = e => { // 接收消息
//    console.log(e.data);
// };

myWorker.postMessage('Greeting from Main.js'); // 向 worker 线程发送消息,对应 worker 线程中的 e.data

// worker.js(worker线程)
self.addEventListener('message', e => { // 接收到消息
    console.log(e.data); // Greeting from Main.js,主线程发送的消息
    self.postMessage('Greeting from Worker.js'); // 向主线程发送消息
});

其他的详细的用法可以参看官网,也可以看其他博主写的,比如关闭worker线程的方法:

复制代码
// main.js(主线程)
const myWorker = new Worker('/worker.js'); // 创建worker
myWorker.terminate(); // 关闭worker

// worker.js(worker线程)
self.close(); // 直接执行close方法就ok了

又比如监听错误的信息:

web worker 提供两个事件监听错误,error (当worker内部出现错误时触发)和 messageerror(当 message 事件接收到无法被反序列化的参数时触发)。

复制代码
// main.js(主线程)
const myWorker = new Worker('/worker.js'); // 创建worker

myWorker.addEventListener('error', err => {
    console.log(err.message);
});
myWorker.addEventListener('messageerror', err => {
    console.log(err.message)
});

// worker.js(worker线程)
self.addEventListener('error', err => {
    console.log(err.message);
});
self.addEventListener('messageerror', err => {
    console.log(err.message);
});

Worker 线程引用其他js文件

总有一些场景,需要放到 worker 进程去处理的任务很复杂,需要大量的处理逻辑,我们当然不想把所有代码都塞到 worker.js 里,那样就太糟糕了。不出意料,web worker 为我们提供了解决方案,我们可以在 worker 线程中利用 importScripts() 方法加载我们需要的js文件,而且,通过此方法加载的js文件不受同源策略约束!

复制代码
// utils.js
const add = (a, b) => a + b;

// worker.js(worker线程)
// 使用方法:importScripts(path1, path2, ...); 

importScripts('./utils.js');

console.log(add(1, 2)); // log 3

记得如果是ESModule 模式的js文档,我们只需要修改一下配置即可,

复制代码
// main.js(主线程)
const worker = new Worker('/worker.js', {
    type: 'module'  // 指定 worker.js 的类型
});

// utils.js
export default add = (a, b) => a + b;

// worker.js(worker线程)
import add from './utils.js'; // 导入外部js

self.addEventListener('message', e => { 
    postMessage(e.data);
});

add(1, 2); // log 3

export default self; // 只需把顶级对象self暴露出去即可

Web Workers 的分类

Web Worker 规范中定义了两类工作线程,分别是专用线程 Dedicated Worker 和共享线程 Shared Worker,其中,Dedicated Worker 只能为一个页面所使用,而 Shared Worker 则可以被多个页面所共享。

Dedicated Worker

一个专用 Worker 仅仅能被生成它的脚本所使用,其浏览器支持情况如下:

(图片来源:[https://caniuse.com/#search=Web Workers\](https://caniuse.com/#search=Web Workers))

需要注意的是,由于 Web Worker 有同源限制,所以在进行本地调试或运行以下示例的时候,需要先启动本地服务器,直接使用 file:// 协议打开页面的时候,会抛出以下异常:

复制代码
Uncaught DOMException: Failed to construct 'Worker': 
Script at 'file:///**/*.js' cannot be accessed from origin 'null'.
1 专用线程 Dedicated Worker:Ping/Pong

「index.html」

复制代码
<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>专用线程 Dedicated Worker ------ Ping/Pong</title>
  </head>
  <body>
    <h3>阿宝哥:专用线程 Dedicated Worker ------ Ping/Pong</h3>
    <script>
      if (window.Worker) {
        let worker = new Worker("dw-ping-pong.js");
        worker.onmessage = (e) =>
          console.log(`Main: Received message - ${e.data}`);
        worker.postMessage("PING");
      } else {
        console.log("呜呜呜,不支持 Web Worker");
      }
    </script>
  </body>
</html>

「dw-ping-pong.js」

复制代码
onmessage = (e) => {
  console.log(`Worker: Received message - ${e.data}`);
  postMessage("PONG");
}

以上代码成功运行后,浏览器控制台会输出以下结果:

复制代码
Worker: Received message - PING
Main: Received message - PONG

每个 Web Worker 都可以创建自己的子 Worker,这允许我们将任务分散到多个线程。创建子 Worker 也很简单,具体我们来看个例子。

2 专用线程 Dedicated Sub Worker:Ping/Pong

「index.html」

复制代码
<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>专用线程 Dedicated Sub Worker ------ Ping/Pong</title>
  </head>
  <body>
    <h3>阿宝哥:专用线程 Dedicated Sub Worker ------ Ping/Pong</h3>
    <script>
      if (window.Worker) {
        let worker = new Worker("dw-ping-pong.js");
        worker.onmessage = (e) =>
          console.log(`Main: Received message - ${e.data}`);
        worker.postMessage("PING");
      } else {
        console.log("呜呜呜,不支持 Web Worker");
      }
    </script>
  </body>
</html>

「dw-ping-pong.js」

复制代码
onmessage = (e) => {
  console.log(`Worker: Received message - ${e.data}`);
  setTimeout(() => {
    let worker = new Worker("dw-sub-ping-pong.js");
    worker.onmessage = (e) => console.log(`Worker: Received from sub worker - ${e.data}`);
    worker.postMessage("PING");
  }, 1000);
  postMessage("PONG");
};

「dw-sub-ping-pong.js」

复制代码
onmessage = (e) => {
  console.log(`Sub Worker: Received message - ${e.data}`);
  postMessage("PONG");
};

以上代码成功运行后,浏览器控制台会输出以下结果:

复制代码
Worker: Received message - PING
Main: Received message - PONG
Sub Worker: Received message - PING
Received from sub worker - PONG
3 专用线程 Dedicated Worker:importScripts

在 Web Worker 中,我们也可以使用 importScripts 方法将一个或多个脚本同步导入到 Web Worker 的作用域中。同样我们来举个例子。

「index.html」

复制代码
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>专用线程 Dedicated Worker ------ importScripts</title>
  </head>
  <body>
    <h3>阿宝哥:专用线程 Dedicated Worker ------ importScripts</h3>
    <script>
      let worker = new Worker("worker.js");
      worker.onmessage = (e) => console.log(`Main: Received kebab case message - ${e.data}`);
      worker.postMessage(
        "Hello, My name is semlinker."
      );
    </script>
  </body>
</html>

importScripts("https://cdn.bootcdn.net/ajax/libs/lodash.js/4.17.15/lodash.min.js");

onmessage = ({ data }) => {
  postMessage(_.kebabCase(data));
};

以上代码成功运行后,浏览器控制台会输出以下结果:

复制代码
Main: Received kebab case message - hello-my-name-is-semlinker
4 专用线程 Dedicated Worker:inline-worker

在前面的例子中,我们都是使用外部的 Worker 脚本来创建 Web Worker 对象。其实你也可以通过 Blob URL 或 Data URL 的形式来创建 Web Worker,这类 Worker 也被称为 Inline Worker。
「1. 使用 Blob URL 创建 Inline Worker」

Blob URL/Object URL 是一种伪协议,允许 Blob 和 File 对象用作图像,下载二进制数据链接等的 URL 源。在浏览器中,我们使用 URL.createObjectURL 方法来创建 Blob URL,该方法接收一个 Blob 对象,并为其创建一个唯一的 URL,其形式为 blob:/,对应的示例如下:

复制代码
blob:https://example.org/40a5fb5a-d56d-4a33-b4e2-0acf6a8e5f641

浏览器内部为每个通过 URL.createObjectURL 生成的 URL 存储了一个 URL → Blob 映射。因此,此类 URL 较短,但可以访问 Blob。生成的 URL 仅在当前文档打开的状态下才有效。它允许引用 、 中的 Blob,但如果你访问的 Blob URL 不再存在,则会从浏览器中收到 404 错误。

复制代码
const url = URL.createObjectURL(
  new Blob([`postMessage("Dedicated Worker created by Blob")`])
);

let worker = new Worker(url);
worker.onmessage = (e) =>
  console.log(`Main: Received message - ${e.data}`);

除了在代码中使用字符串动态创建 Worker 脚本,也可以把 Worker 脚本使用类型为 javascript/worker 的 script 标签内嵌在页面中,具体如下所示:

复制代码
<script id="myWorker" type="javascript/worker">
   self['onmessage'] = function(event) {
     postMessage('Hello, ' + event.data.name + '!');
   };
</script>

接着就是通过 script 对象的 textContent 属性来获取对应的内容,然后使用 Blob API 和 createObjectURL API 来最终创建 Web Worker:

复制代码
<script>
  let workerScript = document.querySelector('#myWorker').textContent;
  let blob = new Blob(workerScript, {type: "text/javascript"});
  let worker = new Worker(URL.createObjectURL(blob));
</script>

「2. 使用 Data URL 创建 Inline Worker」

Data URLs 由四个部分组成:前缀(data:)、指示数据类型的 MIME 类型、如果非文本则为可选的 base64 标记、数据本身:

复制代码
data:[<mediatype>][;base64],<data>

mediatype 是个 MIME 类型的字符串,例如 "image/jpeg" 表示 JPEG 图像文件。如果被省略,则默认值为 text/plain;charset=US-ASCII。如果数据是文本类型,你可以直接将文本嵌入(根据文档类型,使用合适的实体字符或转义字符)。如果是二进制数据,你可以将数据进行 base64 编码之后再进行嵌入。

复制代码
const url = `data:application/javascript,${encodeURIComponent(
  `postMessage("Dedicated Worker created by Data URL")`
)}`;

let worker = new Worker(url);
worker.onmessage = (e) =>
  console.log(`Main: Received message - ${e.data}`);

SharedWorker

SharedWorker 是一种特殊类型的 Worker,可以被多个浏览上下文访问,比如多个 windows,iframes 和 workers,但这些浏览上下文必须同源。它们实现于一个不同于普通 worker 的接口,具有不同的全局作用域:SharedWorkerGlobalScope ,但是继承自WorkerGlobalScope

SharedWorker 线程的创建和使用跟 worker 类似,事件和方法也基本一样。

不同点在于,主线程与 SharedWorker 线程是通过MessagePort建立起链接,数据通讯方法都挂载在SharedWorker.port上。

值得注意的是,如果你采用 addEventListener 来接收 message 事件,那么在主线程初始化SharedWorker() 后,还要调用 SharedWorker.port.start() 方法来手动开启端口。

复制代码
// main.js(主线程)
const myWorker = new SharedWorker('./sharedWorker.js');

myWorker.port.start(); // 开启端口

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

但是,如果采用 onmessage 方法,则默认开启端口,不需要再手动调用SharedWorker.port.start()方法

复制代码
// main.js(主线程)
const myWorker = new SharedWorker('./sharedWorker.js');

myWorker.port.onmessage = msg => {
    console.log(msg.data);
};

以上两种方式效果是一样的,具体信息请参考MessagePort。

由于 SharedWorker 是被多个页面共同使用,那么除了与各个页面之间的数据通讯是独立的,同一个SharedWorker 线程上下文中的其他资源都是共享的。基于这一点,很容易实现不同页面之间的数据通讯。

我们来看一个ShareWorker的例子,它是实现多页面数据共享的例子:

  1. index 页面的 add 按钮,每点击一次,向 sharedWorker 发送一次 add 数据,页面 count 增加1

// index.html

复制代码
<!DOCTYPE html>
<html lang="zh-CN">
    <head>
        <meta charset="utf-8">
        <title>index page</title>
    </head>
    <body>
        <p>index page: </p>
        count: <span id="container">0</span>
        <button id="add">add</button>
        <br>
        // 利用iframe加载
        <iframe src="./iframe.html"></iframe>
    </body>
    <script type="text/javascript">
        if (!!window.SharedWorker) {
            const container = document.getElementById('container');
            const add = document.getElementById('add');
            
            const myWorker = new SharedWorker('./sharedWorker.js');
            
            myWorker.port.start();

            myWorker.port.addEventListener('message', msg => {
                container.innerText = msg.data;
            });

            add.addEventListener('click', () => {
                myWorker.port.postMessage('add');
            });
        }
    </script>
</html>
  1. iframe 页面的 reduce 按钮,每点击一次,向 sharedWorker 发送一次 reduce 数据,页面count 减少1

// iframe.html

复制代码
<!DOCTYPE html>
<html lang="zh-CN">
    <head>
        <meta charset="utf-8">
        <title>iframe page</title>
    </head>
    <body>
        <p>iframe page: </p>
        count: <span id="container">0</span>
        <button id="reduce">reduce</button>
    </body>
    <script type="text/javascript">
        if (!!window.SharedWorker) {
            const container = document.getElementById('container');
            const reduce = document.getElementById('reduce');

            const myWorker = new SharedWorker('./sharedWorker.js');

            myWorker.port.start();
            
            myWorker.port.addEventListener('message', msg => {
                container.innerText = msg.data;
            })

            reduce.addEventListener('click', () => {
                myWorker.port.postMessage('reduce');
            });
        }
    </script>
</html>
  1. sharedWorker 在接收到数据后,根据数据类型处理 num 计数,然后返回给每个已连接的主线程。
    // sharedWorker.js

    let num = 0;
    const workerList = [];

    self.addEventListener('connect', e => {
    const port = e.ports[0];
    port.addEventListener('message', e => {
    num += e.data === 'add' ? 1 : -1;
    workerList.forEach(port => { // 遍历所有已连接的part,发送消息
    port.postMessage(num);
    })
    });
    port.start();
    workerList.push(port); // 存储已连接的part
    port.postMessage(num); // 初始化
    });

结果可以发现,index 页面和 iframe 页面的 count 始终保持一致,实现了多个页面数据同步。

sharedWorker调试

在 sharedWorker 线程里使用 console 打印信息,不会出现在主线程的的控制台中。如果你想调试 sharedWorker,需要在 Chrome 浏览器输入 chrome://inspect/ ,这里能看到所有正在运行的 sharedWorker,然后开启一个独立的 dev-tool 面板。

如果是一个大型项目或者是需要两个不同的界面进行数据传送,那么shareWorker是非常好的一种方式了。

我们也可以将它在项目中进行配置,具体可以看看vite的配置。

参考文章

https://juejin.cn/post/6844904198639714311

https://juejin.cn/post/7139718200177983524

相关推荐
brzhang2 小时前
我觉得可以试试 TOON —— 一个为 LLM 而生的极致压缩数据格式
前端·后端·架构
yivifu2 小时前
JavaScript Selection API详解
java·前端·javascript
这儿有一堆花2 小时前
告别 Class 组件:拥抱 React Hooks 带来的函数式新范式
前端·javascript·react.js
十二春秋3 小时前
场景模拟:基础路由配置
前端
六月的可乐3 小时前
实战干货-Vue实现AI聊天助手全流程解析
前端·vue.js·ai编程
一 乐3 小时前
智慧党建|党务学习|基于SprinBoot+vue的智慧党建学习平台(源码+数据库+文档)
java·前端·数据库·vue.js·spring boot·学习
BBB努力学习程序设计4 小时前
CSS Sprite技术:用“雪碧图”提升网站性能的魔法
前端·html
BBB努力学习程序设计4 小时前
CSS3渐变:用代码描绘色彩的流动之美
前端·html
冰暮流星4 小时前
css之动画
前端·css