webWorker的概念 用法 场景

这里引用上个文章的图片 这里学习下 webWorker

javascript 复制代码
<template>
  <div>
    <div style="display: flex; justify-content: center; align-items: center; margin-top: 20px;">
      <div class="image-container">
        <img src="https://i.imgur.com/cCWBPHi.png" class="final-image">
      </div>
      <div class="image-container">
        <!-- 使用 v-show 确保 canvas 元素始终存在 -->
        <canvas v-show="!isComplete" ref="canvasRef" class="canvas" />
        <img v-show="isComplete" :src="finalImageData" alt="加载完成的图片" class="final-image">
      </div>
    </div>
  </div>
</template>

<script setup>
import { onMounted, ref } from 'vue'

// 定义变量
const canvasRef = ref(null) // canvas 元素引用
const loading = ref(false) // 加载状态
const error = ref(false) // 错误状态
const isComplete = ref(false) // 是否加载完成
const finalImageData = ref('') // 最终图片的 Base64 数据

// 创建 Worker 实例
const worker = new Worker(new URL('./worker.js', import.meta.url), { type: 'module' })

// 监听 Worker 的消息
worker.onmessage = function (event) {
  const { type, data } = event.data

  if (type === 'progress') {
    console.log(`当前分片高度: ${data.chunkHeight}px, 当前绘制位置: y = ${data.y}px`)
  }
  else if (type === 'complete') {
    console.log('图片加载完成!')
    finalImageData.value = data.imageData
    isComplete.value = true
    loading.value = false
    worker.terminate() // 释放 Worker 主线程资源
  }
  else if (type === 'error') {
    console.error('加载图片失败:', data.message)
    error.value = true
    loading.value = false
  }
}

// 点击按钮加载图片
function loadImage() {
  // 确保 canvas 元素已经渲染
  if (!canvasRef.value) {
    console.error('canvas 元素未渲染')
    return
  }

  // 将 canvas 的控制权转移到 OffscreenCanvas
  // OffscreenCanvas 是 HTML5 提供的一个 API,它允许你将 canvas 的渲染操作放到主线程之外的上下文中执行(例如 Web Worker),从而避免阻塞主线程,提升性能
  const offscreenCanvas = canvasRef.value.transferControlToOffscreen()

  // 发送任务给 Worker
  worker.postMessage(
    {
      canvas: offscreenCanvas,
      url: 'https://i.imgur.com/cCWBPHi.png',
      chunkSize: 100,
    },
    [offscreenCanvas],
  )

  loading.value = true
  error.value = false
}

// 在 onMounted 钩子中调用 loadImage,确保 DOM 已渲染
onMounted(() => {
  loadImage()
})
</script>

<style lang="scss" scoped>
.image-container {
  display: flex;
  justify-content: center;
  align-items: center;
  margin-top: 20px;
}

.canvas {
  border: 1px solid #ccc;
  max-width: 100%;
  max-height: 80vh;
  /* 限制 canvas 的最大高度 */
}

.final-image {
  max-width: 100%;
  max-height: 80vh;
  /* 限制图片的最大高度 */
  border: 1px solid #ccc;
}
</style>

下面是 worker.js 文件

其实通信原理和 iframe 一样 只不过可以开启多线程

javascript 复制代码
// 监听主线程的消息  这里的self也就是主线程的window对象
self.onmessage = async function (event) {
  const { canvas, url, chunkSize } = event.data

  try {
    // 加载图片
    const response = await fetch(url)
    const blob = await response.blob()
    const img = await createImageBitmap(blob)

    // 设置 canvas 尺寸
    const width = img.width
    const height = img.height
    canvas.width = width
    canvas.height = height

    const ctx = canvas.getContext('2d')

    // 分片加载逻辑
    let y = 0 // 初始化 y 坐标,表示当前绘制的位置
    const loadNextChunk = function () {
      // 计算当前分片的高度
      const chunkHeight = Math.min(chunkSize, height - y)

      // 绘制当前分片
      ctx.drawImage(img, 0, y, width, chunkHeight, 0, y, width, chunkHeight)
      y += chunkHeight // 更新 y 坐标,准备绘制下一个分片

      // 发送进度信息给主线程
      self.postMessage({
        type: 'progress',
        data: { chunkHeight, y },
      })

      // 如果未加载完,继续加载下一个分片
      if (y < height) {
        requestAnimationFrame(loadNextChunk) // 使用 requestAnimationFrame 优化渲染
      }
      else {
        // 加载完成,将 canvas 转换为图片并发送给主线程
        canvas.convertToBlob().then((blob) => {
          const reader = new FileReader()
          reader.readAsDataURL(blob)
          reader.onloadend = function () {
            self.postMessage({
              type: 'complete',
              data: { imageData: reader.result },
            })
          }
        })
      }
    }

    // 开始加载第一个分片
    loadNextChunk()
  }
  catch (e) {
    console.error('加载图片失败:', e)
    self.postMessage({
      type: 'error',
      data: { message: e.message },
    })
  }
}

今天,我在学习Web Workers的时候,遇到了一些概念,感觉有点复杂。我决定仔细研究一下,看看能不能理解清楚。

首先,我知道Web Workers是一种在后台线程中运行JavaScript代码的方法,可以用来处理复杂的计算任务,从而不阻塞主线程,提升用户体验。但具体的实现方式,我还不太清楚。

我看到代码中使用了new Worker()方法来创建一个Web Worker。这让我想到,是不是在主线程中创建了一个新的线程,专门用来处理特定的任务?好像是这样。然后,主线程通过worker.postMessage()方法向Worker发送任务。这让我联想到Vue.js中父组件和子组件之间的通信,父组件通过$emit方法向子组件传递数据,而子组件通过事件监听来接收数据。难道这里也是类似的机制?

于是,我查找了一些资料,发现确实,postMessage方法在主线程和Worker之间传递数据,就像组件之间的事件传递一样。主线程发送消息,Worker接收并处理,然后通过self.postMessage()将结果发送回去。这确实有点像Vue2中的父子传值,只不过这里的"子"是Worker线程。

接下来,我看到在主线程中使用了worker.onmessage来监听Worker的消息。这部分有点让我困惑。我想,onmessage是不是一种事件监听器,当Worker发送消息回来时,主线程就能接收到并处理?这和Vue中的事件处理很像,父组件监听子组件的事件,当子组件触发事件时,父组件执行相应的处理函数。

为了更好地理解,我尝试写了一个简单的例子。主线程创建一个Worker,然后通过postMessage发送一个计算任务,Worker接收到任务后进行计算,然后通过postMessage把结果送回主线程。主线程监听到消息后,处理结果并显示出来。运行后,发现确实有效,计算任务在后台线程完成,主线程继续处理其他事情。

但是,我也有疑问。比如,Worker和主线程之间的通信是不是只能通过postMessage?有没有其他方式?另外,self在Worker中的作用是什么?为什么不能用this或者其他变量?

我查阅了相关文档,了解到在Worker中,self是指向Worker全局对象的,可以用它来发送和接收消息。而主线程和Worker之间的通信只能通过postMessage方法,这是为了确保线程安全,避免共享内存带来的问题。

还有一个问题,如果主线程和Worker之间传递的是对象,会发生什么?会不会导致数据共享的问题?文档中提到,传递的是结构化克隆,而不是引用,因此不会共享内存,每个线程都有自己的数据副本,这样就避免了竞态条件和数据不一致的问题。

通过这个学习过程,我对Web Workers有了更深入的理解,特别是它们和Vue组件之间的类比,帮助我更好地记忆和应用这些概念。虽然有些细节还需要进一步探索,但总体上,我觉得自己已经掌握了一个基本的框架。

总结:

使用 new Worker() 方法在主线程创建了一个 Web Worker,主线程通过 worker.postMessage() 发送任务给 Worker,并在主线程开启 worker.onmessage 监听 Worker 的消息。这与 Vue2 中的父子组件通信机制相似,主线程类似于父组件,Worker 线程类似于子组件。Worker.js 文件中通过 self.postMessage() 将结果发送回主线程,主线程在接收到消息后进行处理。

相关推荐
组合缺一1 天前
(对标 Spring)OpenSolon v3.7.0, v3.6.4, v3.5.8, v3.4.8 发布(支持 LTS)
java·后端·spring·web·solon
闲人编程2 天前
从零开发一个简单的Web爬虫(使用Requests和BeautifulSoup)
前端·爬虫·beautifulsoup·bs4·web·request·codecapsule
青山的青衫2 天前
【JavaWeb】Tlias后台管理系统
java·web
qq_398586543 天前
Utools插件实现Web Bluetooth
前端·javascript·electron·node·web·web bluetooth
光影少年3 天前
WEBNN是什么,对前端工程带来哪些优势
前端·web3·web
笛秋白4 天前
快速了解搭建网站流程——全栈网站搭建指南
团队开发·web·web开发·全栈·网站开发
vortex58 天前
用 Scoop 快速部署 JeecgBoot 开发环境:从依赖安装到服务管理
java·windows·springboot·web·开发·jeecg-boot
lally.9 天前
2025强网杯web wp
web
2401_841495649 天前
黑客攻击基础知识
网络·黑客·操作系统·web·计算机结构·应用程序·黑客攻击
任风雨11 天前
11.9.14.ServletContext
web