记一次Web Worker的使用

需求背景

当渲染大量卡片时,需要从卡片中的图片提取主色调,并据此计算合适的文本颜色。这一过程涉及大量计算,容易造成主线程阻塞,影响页面流畅度,尤其在处理多张图片或高分辨率图片时,存在性能问题。

优化方案

为了提升计算效率,采用 Web Worker 将颜色计算逻辑移至独立线程,利用浏览器的多线程能力,实现主色调提取与文本颜色计算的异步处理。从而避免主线程阻塞,提升页面性能。

主要逻辑

  1. 使用web worker进行图片取色&文本色计算
  2. 共用一个worker
  3. 抽离第三方库的代码到worker中

首先讲一下为什么需要将第三方库的代码进行抽离,例如取图片主色调的第三方库 color-thief

这段代码不能在worker中执行,原因如下:

Worker 不能访问 DOM Web Worker 运行在独立线程中,无法访问 documentwindowcanvas 等 DOM API,因此 document.createElement('canvas') 无法在 Worker 中执行。

所以我们不能直接在worker中操作dom,如果要在Worker 中处理图片颜色数据,可以使用 OffscreenCanvas,它允许在 Web Worker 线程中创建和操作 Canvas。那么就有两种方案:

方案 方案 1:Worker 直接 fetch 加载图片 方案 2:主线程加载图片,传数据给 Worker
加载方式 Worker 内部 fetch(src) 下载图片 主线程 new Image().src = src
缓存机制 无法使用浏览器 Disk Cache,每次都可能重新下载 能利用浏览器缓存,避免重复下载
CORS 影响 可能遇到 CORS 限制,需要手动处理跨域 主线程加载 crossOrigin="anonymous" 解决 CORS 问题
图片解码 需要 fetch → Blob → createImageBitmap 额外转换 直接 Image 解码,更高效
Worker 计算负担 Worker 需 下载 + 解码 + 颜色计算,任务重 Worker 仅计算颜色,更轻量
主线程占用 低(Worker 负责所有操作) 需要主线程加载 Image 和 Canvas
性能 解码 & 计算全在 Worker,但下载可能慢 加载快、缓存友好,Worker 专注计算,整体更快
ini 复制代码
// 方案一worker.js
self.onmessage = async (e) => {
  const imageUrl = e.data;

  const res = await fetch(imageUrl); // Worker 自己下载
  const blob = await res.blob();
  const bitmap = await createImageBitmap(blob); // 解码为图像

  const canvas = new OffscreenCanvas(bitmap.width, bitmap.height);
  const ctx = canvas.getContext('2d');
  ctx.drawImage(bitmap, 0, 0);

  const imageData = ctx.getImageData(0, 0, bitmap.width, bitmap.height);
  // 👉 在这里做颜色计算

  self.postMessage({ message: '颜色计算完了' });
};

因为我们最好是能够利用浏览器的缓存,而且需要避免跨域,最好采用方案二去实现:

在主线程中,我们加载图片,获取图片数据,通过postMessage传递给worker

ini 复制代码
const img = new Image();
    img.src = src;
    img.crossOrigin = 'anonymous';
    img.onload = () => {
      const canvas = new OffscreenCanvas(img.width, img.height);
      const ctx = canvas.getContext('2d')!;
      ctx.drawImage(img, 0, 0);
      const imageData = ctx.getImageData(0, 0, img.width, img.height);
      const buffer = imageData.data.buffer;
      this.instance.postMessage(
        {
          key: src,
          buffer,
          src,
          type: COLOR_TYPE.BOTH,
          width: imageData.width,
          height: imageData.height,
        },
        [buffer],
      );

这里为什么要通过第二个参数传递buffer数据呢?

postMessage(aMessage, transferList) 是主线程与 Web Worker 之间通信的方法:

  • aMessage 是要发送的数据,默认通过结构化拷贝(深拷贝) 的方式传递,发送的是副本,发送后双方可以独立修改;
  • transferList 是一个可选的"可转移对象"数组 (如 ArrayBufferMessagePortImageBitmap 等),用于转移对象的所有权;被转移的对象不会被复制,而是直接从主线程"搬"到 Worker,发送后原上下文将无法再使用该对象,性能更高效;

在worker中拿到图片数据,对图片进行图片提取主色调,并据此计算合适的文本颜色。

我在worker中主要是把这段代码通过blob的形式实现。

在主线程只需要new Worker(workerScript)即可。

目前使用后存在一些问题,如果有人知道怎么解决,强烈欢迎来帮助我解决一下🙏🙏🙏

遇到的问题

  1. web worker不支持导入的时候使用变量,这样会导致报错

webpack和rspack 均不支持在worker中使用变量

2、在worker中通过import引入其他文件,在打包到测试环境上后,会报安全问题,目前不知道咋解决.

这样就很烦,解析图片主色调和计算对比度的算法就是从第三方库的代码里扒出来的,然后这样就导致一个文件里面巨多代码

本人采取过的尝试

  1. 参考了rspack的例子,在本地通过import 引入没有问题,但是打包测试环境就不行了

github.com/rspack-cont...

  1. 通过importScript导入,ts会直接报错==>在tsconfig.json引入一下可以解决报错。但是没用,importScript不能加载模块化的文件。导入的文件不能通过export导出

既然都把别人的第三方库代码扒下来了,取色和对比度算法也不是很复杂,接下来我们就来探究下这两个方法的原理吧,worker就先说到这。

参考文档:

www.freecodecamp.org/chinese/new...

github.com/puxiao/note...

相关推荐
柒和远方10 分钟前
Phase 7.4 学习博客:为什么多 API 项目需要 Swagger / OpenAPI
前端·后端·架构
张龙68713 分钟前
拼多多开放平台对接踩坑实录:从 CLIENT_ID 配置到 MD5 签名算法的完整填坑指南
前端
GuWenyue20 分钟前
提示词彻底过时?一套上下文工程方案,3步让LLM落地生产,代码直接复用
前端·javascript·人工智能
柒和远方24 分钟前
Phase 7.3 复盘:后台任务不只是“扔进队列”,还要能被看见
前端·后端·架构
2501_9437823528 分钟前
【共创季稿事节】 倒计时器:时分秒选择器与定时器的协同工作
前端·华为·harmonyos·鸿蒙·鸿蒙系统
奶油mm36 分钟前
公司技术债堆积如山,我一人之力用 Vue3 偷换了整个前端架构
前端·vue.js
用户9385156350736 分钟前
深入理解 JavaScript 中的 this 与数据存储的奥秘
前端·javascript
爱喝水的鱼丶1 小时前
SAP-ABAP:接口 vs 抽象类:ABAP OOP两类扩展方式的差异与选型原则
运维·性能优化·sap·abap·erp·经验交流
JNX_SEMI1 小时前
AT2659 L1频段多模卫星导航低噪声放大器技术解析
前端·单片机·嵌入式硬件·物联网·硬件工程
Profile排查笔记3 小时前
指纹浏览器环境异常排查:Fingerprint、Profile、Proxy、Session 和 Task Log 怎么看
前端·人工智能·后端·自动化