将 Google Comlink 用于具有 React/Typescript 的网络工作者会导致 DOM 异常

Posted

技术标签:

【中文标题】将 Google Comlink 用于具有 React/Typescript 的网络工作者会导致 DOM 异常【英文标题】:Using Google Comlink for web workers with React/Typescript results in DOM Exception 【发布时间】:2021-12-11 09:25:38 【问题描述】:

我目前有一个使用 papaparse 和 React/Typescript 堆栈的 CSV 解析应用程序,并且我正在尝试将其中一些工作卸载给网络工作者。我在这个文件中设置了工人:

import  parseFunc  from '../src/parsing'

const exported = 
  parseFunc,

export type ParseWorker = typeof exports

expose(exported)

连同关联的 tsconfig.json 在同一目录中:


  "compilerOptions": 
    "strict": true,
    "target": "esnext",
    "module": "esnext",
    "lib": [
      "webworker",
      "esnext"
    ],
    "moduleResolution": "node",
    "noUnusedLocals": true,
    "sourceMap": true,
    "allowJs": false,
    "baseUrl": "."
  

然后我在这里加载并尝试运行它:

  import  wrap  from 'comlink'
  const worker = new Worker('../../../customWebWorkers', 
    name: 'customWebWorkers',
    type: 'module',
  )
  const workerApi = wrap<import('../../../customWebWorkers').ParseWorker>(
    worker
  )
  return await workerApi.parseFunc(
    csvData
  )

在本地运行它就可以了!当我尝试将我的应用程序推送到当前运行的版本时,问题出现了,我遇到了这个错误:

Uncaught (in promise) DOMException: Failed to construct 'Worker':
Script at 'https://myWebStorage/customWebWorkers.132234d2.chunk.worker.js' 
cannot be accessed from origin 'https://csv-parser-132.com'.
    at...

我在网上查看了一些替代解决方案,最常见的两个是使用 Blob 获取脚本对象的 url 并以这种方式加载它,或者使用 importScripts:不幸的是,这些似乎都不适用于我的用例。如果我尝试这些解决方案,则会收到此错误:

Failed to load module script: Expected a javascript module script but the server responded with a MIME type of "text/html". Strict MIME type checking is enforced for module scripts per HTML spec.

我认为这是由于上述解决方案没有包含 tsconfig 文件。

我知道这是一个相当困难的问题,但任何可能的答案将不胜感激!我很惊讶在 typescript/React 堆栈中运行 web worker 如此困难。

【问题讨论】:

【参考方案1】:

我在one of my projects 中遇到了同样的问题,我使用了相同的设置(React + TypeScript + Comlink)。模块加载与 Web Worker 的工作方式略有不同,因此您不能像往常一样使用导入和导出。

幸运的是,谷歌还有一个名为comlink-loader 的库可以解决这个问题。您必须将要workerize的脚本从customWebWorkers 重命名为customWebWorkers.worker.js。 Comlink Loader 会在编译过程中自动获取它并创建一个包装类,然后您可以使用import ParseWorker from 'comlink-loader!../../../customWebWorkers' 导入它。然后,您可以像往常一样使用这个包装的类来执行您的方法。

import ParseWorker from 'comlink-loader!../../../customWebWorkers'

const workerFactory = new ParseWorker()

const worker = await new workerFactory.ParseWorker()

const result = await parseWorker.parseFunc(csvData)

注意 1:在这个例子中有两种类型的 ParseWorker 类。顶部是一个工厂,可用于创建任意数量的实例。基本上,每个实例都是其自己的 Web Worker,独立于其他实例运行,如果您想在所有 CPU 内核上并行化工作负载,这将非常有用。如果这对您的用例来说太过分了,您还可以在singleton mode 中使用 Comlink Loader。这为每个模块提供了一个 Web Worker。

注意 2:构造函数和类中的任何方法都需要等待,因为它们已被包装在异步方法中。

注意 3:如果您的 csvData 变量的内容很大,您可能需要使用 Comlink.transfer 来防止重复数据的开销。

最后,但这更多是个人观察,我在浏览器中使用 Web Workers 的经验只是马马虎虎。我希望卸载 CPU 繁重的代码以提高性能并减少 UI 中的延迟,它确实做到了.. 但它有时也使 UI “紧张”,有时“停滞”整个窗口。也许它们在服务器端 NodeJS 上下文中工作得更好.. 不确定..

【讨论】:

嘿,谢谢!正如你所说,我重新设计了所有使用 comlink 加载程序,但现在我看到了一个完全不同的问题;每当我尝试使用此错误启动它时,加载器似乎都会失败:` ./src/utils/parse.worker.ts 中的错误(./node_modules/comlink-loader/dist/comlink-worker-loader.js !./src/utils/parse.worker.ts) 33:9 模块解析失败:意外令牌 (33:9) 使用这些加载器处理文件:* ./node_modules/comlink-loader/dist/comlink-worker-loader .js`知道这是怎么回事吗?你遇到过类似的问题吗?

以上是关于将 Google Comlink 用于具有 React/Typescript 的网络工作者会导致 DOM 异常的主要内容,如果未能解决你的问题,请参考以下文章

Web Workers RPC:Comlink 源码解析

Web Workers RPC:Comlink 源码解析

使用 Geoplugin 旋转链接

Web 多线程开发利器 Comlink 的剖析与思考

如何在 Android 应用程序中集成 Google Analytics

用于具有单个回调 url 的多租户应用程序的 ASP.NET Core Google Auth?