记一次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...

相关推荐
AI浩21 小时前
【Labelme数据操作】LabelMe标注批量复制工具 - 完整教程
运维·服务器·前端
涔溪1 天前
CSS 网格布局(Grid Layout)核心概念、基础语法、常用属性、实战示例和进阶技巧全面讲解
前端·css
2401_878454531 天前
浏览器工作原理
前端·javascript
西陵1 天前
为什么说 AI 赋能前端开发,已经不是选择题,而是必然趋势?
前端·架构·ai编程
国科安芯1 天前
AS32S601型MCU芯片电源管理(PMU)模块详解
单片机·嵌入式硬件·性能优化·架构·risc-v
by__csdn1 天前
Vue3 setup()函数终极攻略:从入门到精通
开发语言·前端·javascript·vue.js·性能优化·typescript·ecmascript
天天扭码1 天前
前端如何实现RAG?一文带你速通,使用RAG实现长期记忆
前端·node.js·ai编程
Luna-player1 天前
在前端中,<a> 标签的 href=“javascript:;“ 这个是什么意思
开发语言·前端·javascript
lionliu05191 天前
js的扩展运算符的理解
前端·javascript·vue.js
小草cys1 天前
项目7-七彩天气app任务7.4.2“关于”弹窗
开发语言·前端·javascript