浏览器|浏览器多线程离屏渲染压缩打包方案( 二 )


)worker.addEventListener('message' (event:MessageEvent<ZipWorkerRequestEventData>) => {  const data = event.data  if (data.type === 'zip') {    doZip(data.arraybuffer)  )
编写完成后 , 然后再使用webpack 的 worker-loader 加载进来使用:
import MainWorker from 'worker-loader!@/workers/main.worker'
此时页面代码摘要为:
// vue3 tscanvas.toBlob((blob)=>{  blob.arrayBuffer().then((ab)=>{    const message: ZipWorkerRequestEventData = {      type: 'zip'      arraybuffer: ab        worker.postMessage(message [ab
)  ))    worker.onmessage = (event: MessageEvent<ZipWorkerResponseEventData>) => {  const data = event.data  if (data.type === 'save') {    const blob = new Blob([data.content as ArrayBuffer
)    saveAs(blob 'test.zip')    // 下载成功!           else if (data.type === 'percent') {    // 进度条        percent.value = data.percent ?? 0  
有点要特别注意: postMessage 推荐使用对象转移的写法 。 这里要解释一下 , 主线程与 Web Workers 之间的通信 , 并不是对象引用的传递 , 而是序列化/反序列化的过程 , 当对象非常庞大时 , 序列化和反序列化都会消耗大量计算资源 , 降低运行速度 。 对象转移就是将对象引用零成本转交给 Web Workers 的上下文 , 而不需要进行结构拷贝 。


需要注意的是 , 对象引用转移后 , 原先上下文就无法访问此对象了 , 需要在 Web Workers 再次将对象还原到主线程上下文后 , 主线程才能正常访问被转交的对象 。
其中可以进行引用转移的对象 , 只有:
  • ArrayBuffer
  • MessagePort
  • ReadableStream
  • WritableStream
  • TransformStream
  • AudioData
  • ImageBitmap
  • VideoFrame
  • OffscreenCanvas
详见
这也是我们把图片转化为 ArrayBuffer 对象进行传输的主要原因 。 不然复制一份数据 , 既浪费内存又浪费算力 。
通过这种方案 , 就把压缩这一部分的计算转移到了 Web Worker 那里去 , 从而避免了JS主线程的阻塞 。 但是这适用场景不是那么多 , 因为这对于批量小图片的压缩打包 , 收益不是很大 , 另外生成图片也非常耗时耗资源 , 这一部分并没有解决 , 所以接下来我们尝试把 Canvas 图片渲染生成 也放入 Web Worker 那里去进行做 。
OffscreenCanvas Worker 离屏渲染

  1. Web Worker 全局作用域中不存在 Image
  2. 有绘制过的 Canvas 无法转化为 OffscreenCanvas 会报出: Failed to execute 'transferControlToOffscreen' on 'HTMLCanvasElement': Cannot transfer control from a canvas that has a rendering context. 错误
方案
首先添加 @types 以引入智能提示: yarn add -D @types/offscreencanvas
另外确立js主线程和 web worker 之间的传输以 ImageBitmap 对象的形式进行图像传递 。
为什么?
首先 ImageBitmap 是 Transferable(见上部分Worker多线程压缩:引用转移介绍)

相关经验推荐