阿卡不拉阿卡不拉
Vue3
阿卡的博客
Vue3
阿卡的博客
  • Vue3

    • 快速入门

      • 搭建工程 👌
      • 模板语法
      • 响应式基础
      • 响应式常用 API
      • 计算属性
      • 类与样式绑定
      • 条件和列表渲染
      • 事件处理
      • 表单处理
      • 生命周期
      • 侦听器
      • 组件介绍
      • Props
      • 自定义事件
      • 组件v-model
      • 插槽
      • 前端路由介绍
      • KeepAlive内置组件
      • 状态管理库
      • 组件库介绍
    • 深入本质

      • 虚拟DOM本质
      • 模板的本质
      • 组件树和虚拟DOM树
      • 数据拦截的本质
      • 响应式数据的本质
      • 响应式的本质
      • 响应式和组件渲染
      • 实现响应式系统 1
      • 实现响应式系统 2
      • 图解EFFECT
      • 实现响应式系统 3
      • 手写computed
      • 手写watch
      • 指令的本质
      • 插槽的本质
      • v-model的本质
      • setup 语法标签
      • 组件生命周期
      • keepalive 生命周期
      • keepalive的本质
      • key的本质
    • 细节补充

      • 【Vue】属性透传
      • 【Vue】依赖注入
      • 【Vue】组合式函数 👌
      • 【Vue】自定义指令
      • 【Vue】插件
      • 【Vue】Transition
      • 【Vue】TransitionGroup
      • 【Vue】Teleport
      • 【Vue】异步组件
      • 【Vue】Suspense
      • 【Router】路由模式
      • 【Router】路由零碎知识
      • 【Router】路由匹配语法
      • 【Router】路由组件传参
      • 【Router】内置组件和函数
      • 【Router】导航守卫
      • 【Router】过渡特效
      • 【Router】滚动行为
      • 【Router】动态路由
      • 【State】通信方式总结
      • 【State】Pinia 自定义插件
      • 【场景】封装树形组件
      • 【场景】自定义 ref 实现防抖
      • 【场景】懒加载
      • 【场景】虚拟列表
      • 【场景】虚拟列表优化
      • 【第三方库】VueUse
      • 【第三方库】vuedragable
      • 【第三方库】vue-drag-resize
      • 【第三方库】vue-chartjs
      • 【第三方库】vuelidate
      • 【第三方库】vue3-lazyload
      • 【场景】Websocket 聊天室
      • 【Vite】✨ 认识 Vite👌
      • 【Vite】配置文件 👌
      • 【Vite】✨ 依赖预构建 👌
      • 【Vite】构建生产版本 👌
      • 【Vite】环境变量与模式
      • 【Vite】CLI
      • 【Vite】Vite 插件
  • 笔面试

    • HTML

      • HTML 面试题汇总
      • 文档声明
      • 语义化
      • W3C 标准组织
      • SEO
      • iframe
      • 微格式
      • 替换元素
      • 页面可见性
    • CSS

      • CSS 面试题汇总
      • CSS 单位总结
      • 居中方式总结
      • 隐藏元素方式总结
      • 浮动
      • 定位总结
      • BFC
      • CSS 属性计算过程
      • CSS 层叠继承规则总结
      • @import 指令
      • CSS3 calc 函数
      • CSS3 媒体查询
      • 过渡和动画事件
      • 渐进增强和优雅降级
      • CSS3 变形
      • 渐进式渲染
      • CSS 渲染性能优化
      • 层叠上下文
      • CSS3 遮罩
    • JavaScript

      • JavaScript 面试题汇总
      • ✨ let、var、const 的区别
      • JS中的数据类型
      • 包装类型
      • 数据类型的转换
      • 运算符
      • ✨ 原型链
      • ✨ this 指向
      • ✨ 垃圾回收与内存泄漏
      • ✨ 执行栈和执行上下文
      • ✨ 作用域和作用域链
      • ✨ 闭包
      • DOM 事件的注册和移除
      • DOM 事件的传播机制
      • 阻止事件默认行为
      • 递归
      • ✨ 属性描述符
      • class 和构造函数区别
      • 浮点数精度问题
      • 严格模式
      • ✨ 函数防抖和节流
      • ✨ WeakSet 和 WeakMap
      • ✨ 深浅拷贝
      • 函数柯里化
      • Node 事件循环
      • 尺寸和位置
    • 浏览器

      • 浏览器面试题汇总
      • ✨ 浏览器的渲染流程
      • ✨ 资源提示关键词
      • 浏览器的组成部分
      • IndexedDB
      • ✨ File API
      • ✨ 浏览器缓存
      • ✨ 浏览器跨标签页通信
      • Web Worker
    • 网络

      • 网络面试题汇总
      • 五层网络模型 👌
      • 常见请求方法 👌
      • ✨cookie👌
      • 面试题
      • 加密
      • ✨JWT👌
      • ✨ 同源策略及跨域问题 👌
      • 文件上传
      • ✨ 输入 url 地址之后
      • 文件下载
      • ✨ session
      • ✨ TCP
      • ✨ CSRF 攻击
      • ✨XSS 攻击 👌
      • ✨ 网络性能优化
      • 断点续传
      • 域名和 DNS
      • SSL、TLS、HTTPS 的关系
      • ✨ HTTP 各版本差异 👌
      • HTTP 缓存协议
      • ✨ WebSocket
    • 工程化

      • CMJ 和 ESM
      • npx
      • ESLint
    • Vue2

      • Vue 面试题汇总相关
      • 组件通信方式总结
      • 虚拟 DOM
      • v-model
      • 数据响应式原理
      • diff
      • 生命周期详解
      • computed
      • 过滤器
      • 作用域插槽
      • 过度和动画
      • 优化
      • keep-alive
      • 长列表优化
      • 其他 API
      • 模式和环境变量
      • 更多配置
      • 更多命令
      • 嵌套路由
      • 路由切换动画
    • Vue3

      • ✨ Vue3 整体变化 👌
      • ✨ Vue3 响应式变化 👌
      • ✨ nextTick 实现原理 👌
      • 两道代码题 👌
      • Vue 运行机制
      • 渲染器核心功能
      • 事件绑定与更新

Web Worker

在运行大型或者复杂的 JavaScript 脚本的时候经常会出现浏览器假死的现象,这是由于 JavaScript 这个语言在执行的时候是采用单线程来执行而导致的。采用同步执行的方式进行运行,如果出现阻塞,那么后面的代码将不会执行。例如:

while (true) {}

那么能不能让这些代码在后台运行,或者让 JavaScript 函数在多个进程中同时运行呢?

HTML5 所提出的 Web Worker 正是为了解决这个问题而出现的。

HTML5 的 Web Worker 可以让 Web 应用程序具备后台的处理能力。它支持多线程处理功能,因此可以充分利用多核 CPU 带来的优势,将耗时长的任务分配给 HTML5 的 Web Worker 运行,从而避免了有时页面反应迟缓,甚至假死的现象。

本文将分为以下几个部分来介绍 Web worker:

  • Web Worker 概述
  • Web Worker 使用示例
  • 使用 Web Worker 实现跨标签页通信

Web worker 概述

在 Web 应用程序中,Web Worker 是一项后台处理技术。

在此之前,JavaScript 创建的 Web 应用程序中,因为所有的处理都是在单线程内执行的,所以如果脚本所需运行时间太长,程序界面就会长时间处于停止状态,甚至当等待时间超出一定的限度,浏览器就会进入假死的状态。

为了解决这个问题,HTML5 新增加了一个 Web Worker API。

**使用这个 API,用户可以很容易的创建在后台运行的线程,这个线程被称之为 Worker。**如果将可能耗费较长时间的处理交给后台来执行,则对用户在前台页面中执行的操作没有影响。

Web Worker 的特点如下:

  • 通过加载一个 JS 文件(Worker)来进行大量复杂的计算,而不挂起主进程。通过 postMessage 和 onmessage 进行通信。

  • 可以在 Worker 中通过 importScripts(url) 方法来加载 JavaScript 脚本文件。

  • 可以使用 setTimeout( ),clearTimeout( ),setInterval( ) 和 clearInterval( ) 等方法。

  • 可以使用 XMLHttpRequest 进行异步请求。

  • 可以访问 navigator 的部分属性。

  • 可以使用 JavaScript 核心对象。

除了上述的优点,Web Worker 本身也具有一定局限性的,具体如下:

  • 不能跨域加载 JavaScript

  • Worker 内代码不能访问 DOM

  • 使用 Web Worker 加载数据没有 JSONP 和 Ajax 加载数据高效。

目前来看,Web Worker 的浏览器兼容性还是很不错的,基本得到了主流浏览器的一致支持。

在开始使用 Web Worker 之前,我们先来看一下使用 Worker 时会遇到的属性和方法,如下:

  • self:self 关键值用来表示本线程范围内的作用域。

  • postMessage:向创建线程的源窗口发送信息。

  • onmessage:获取接收消息的事件句柄。

  • importScripts(urls):Worker 内部如果要加载其他脚本,可以使用该方法来导入其它 JavaScript 脚本文件。参数为该脚本文件的 URL 地址,导入的脚本文件必须与使用该线程文件的页面在同一个域中,并在同一个端口中。

例如:

// 导入了 3 个 JavaScript 脚本
importScripts("worker1.js", "worker2.js", "worker3.js");

Web Worker 使用示例

接下来我们来看一下 Web Worker 的具体使用方式。

Web Worker 的使用方法非常简单,只需要创建一个 Web Worker 对象,并传入希望执行的 JavaScript 文件即可。

之后在页面中设置一个事件监听器,用来监听由 Web Worker 对象发来的消息。

如果想要在页面与 Web Worker 之间建立通信,数据需要通过 postMessage( ) 方法来进行传递。

创建 Web Worker。步骤十分简单,只要在 Worker 类的构造器中,将需要在后台线程中执行的脚本文件的 URL 地址作为参数传入,就可以创建 Worker 对象,如下:

let worker = new Worker("./worker.js");

注意:在后台线程中是不能访问页面或者窗口对象的,此时如果在后台线程的脚本文件中使用 window 或者 document 对象,则会引发错误。

这里传入的 JavaScript 的 URL 可以是相对或者绝对路径,只要是相同的协议,主机和端口即可。

如果想获取 Worker 进程的返回值,可以通过 onmessage 属性来绑定一个事件处理程序。如下:

let worker = new Worker("./worker.js");
worker.onmessage = function () {
  console.log("the message is back!");
};

这里第一行代码用来创建和运行 Worker 进程,第 2 行设置了 Worker 的 message 事件,当后台 Worker 的 postMessage 方法被调用时,该事件就会被触发。

使用 Worker 对象的 postMessage 方法可以给后台线程发送消息。发送的消息需要为文本数据,如果要发送任何 JavaScript 对象,需要通过 JSON.stringify( ) 方法将其转换成文本数据。

worker.postMessage(message);

通过获取 Worker 对象的 onmessage 事件以及 Worker 对象的 postMessage 方法就可以实现线程之间的消息接收和发送。

Web Worker 不能自行终止,但是能够被启用它们的页面所终止。

调用 terminate( ) 函数可以终止后台进程。被终止的 Web Workers 将不再响应任何消息或者执行任何其他运算。

终止之后,Worker 不能被重新启动,但是可以使用同样的 URL 创建一个新的 Worker。

下面是 Web Worker 的一个具体使用示例。

<p>计数:<output id="count"></output></p>
<button id="startBtn">开始计数</button>
<button id="endBtn">结束计数</button>
const doms = {
  count: document.getElementById("count"),
  startBtn: document.getElementById("startBtn"),
  endBtn: document.getElementById("endBtn"),
};
let worker;
startBtn.onclick = function () {
  worker = new Worker("worker.js");
  // 接收来自于后台的数据
  worker.onmessage = function (e) {
    doms.count.innerHTML = e.data;
  };
};

endBtn.onclick = function () {
  worker.terminate();
  worker = null;
};

worker.js

let count = 0;

setInterval(function () {
  count++;
  postMessage(count);
}, 1000);

在上面的代码中,当用户点击"开始工作"时,会创建一个 Web Worker 在后台进行计数。每次计的数都会通过 postMessage 方法返回给前台。

当用户点击"停止工作"时,则会调用 terminate( ) 方法来终止 Web Worker 的运行。

使用 Web Worker 实现跨标签页通信

Web Worker 可分为两种类型:

  • 专用线程 dedicated Web worker

  • 共享线程 shared Web worker

Dedicated Web worker 随当前页面的关闭而结束,这意味着 Dedicated Web worker 只能被创建它的页面访问。

与之相对应的 Shared Web worker 共享线程可以同时有多个页面的线程链接。

前面我们示例 Web Worker 时,实例化的是一个 Worker 类,这就代表是一个 Dedicated Web worker,而要创建 Shared Worker 则需要实例化 SharedWorker 类。

var worker = new SharedWorker("sharedworker.js");

下面我们就使用 Shared Web worker 共享线程来实现跨标签页通信。

<body>
  <input type="text" name="" id="content" />
  <button id="btn">发送数据</button>
  <script>
    const content = document.querySelector("#content");
    const btn = document.querySelector("#btn");

    // 创建一个worker
    const worker = new SharedWorker("worker.js");
    // 点击向worker发送消息
    btn.onclick = function () {
      worker.port.postMessage(content.value);
    };
  </script>
</body>
<body>
  <script>
    const worker = new SharedWorker("worker.js");
    worker.port.start();

    // 监听worker返回的数据
    worker.port.onmessage = function (e) {
      if (e.data) {
        console.log(e.data);
      }
    };

    // 轮询向worker发送消息查询数据
    setInterval(function () {
      worker.port.postMessage("get");
    }, 1000);
  </script>
</body>
let data = ""; // 存储用户发送的信息
self.onconnect = function (e) {
  console.log("页面连接上了");
  let port = e.ports[0];
  port.onmessage = function (e) {
    // 说明要将接收到的数据返回给客户端
    if (e.data === "get") {
      port.postMessage(data);
      data = "";
    } else {
      data = e.data;
    }
  };
};
最近更新:: 2025/7/14 08:40
Contributors: AK
Prev
✨ 浏览器跨标签页通信