将 Web Worker 与 React 和 Webpack 5 一起使用

Using Web Workers with React and Webpack 5

提问人:Th0rgal 提问时间:1/21/2022 最后编辑:vsyncTh0rgal 更新时间:8/9/2022 访问量:5368

问:

几天来,我一直在尝试将 Web Workers 与 react 一起使用,但遇到了一些问题。

我从使用 webpack 4 的 create-react-app 开始。我可以通过本教程使用Web Worker: https://javascript.plainenglish.io/web-worker-in-react-9b2efafe309c 以这种方式加载WebWorker:

export default class WorkerBuilder extends Worker {
  constructor(worker) {
    const code = worker.toString();
    const blob = new Blob([`(${code})()`]);
    return new Worker(URL.createObjectURL(blob));
  }
}

不幸的是,我需要使用我的工人 (d3-delaunay) 的库,而 webpack 在我尝试这样做时给了我一个错误(我认为它改变了它的路径)。

我听说过 worker-loader,我了解到它已经被弃用了,因为 Web Worker 导入现在是在 webpack 5 中构建的。

我将我的应用程序更新到 WebPack 5(如 create-react-app wiki 中所述),这并不容易,因为我的很多依赖项都坏了。

但是当我尝试加载我的 WebWorker 时,如此处所述: https://webpack.js.org/guides/web-workers/ 这根本不起作用(没有错误)。

这是我的代码:

console.log("hello");
const worker = new Worker(new URL('./world.worker.js', import.meta.url));
worker.postMessage({
    question:
        'The Answer to the Ultimate Question of Life, The Universe, and Everything.',
});
worker.onmessage = ({ data: { answer } }) => {
    console.log(answer);
};
console.log("world");

还有我的world.worker.js:

import { Delaunay } from "d3-delaunay";

// eslint-disable-next-line import/no-anonymous-default-export
export default () => {
    // eslint-disable-next-line no-restricted-globals
    self.onmessage = (message) => {
        console.log("yolo");
    };
};

但我唯一的输出是:

hello
world

我的工人似乎没有工作。

javascript reactjs webpack web-worker

评论

0赞 Shannon Cole 5/3/2022
您是否找到了使用 WebWorkers 的好解决方案?我正在尝试找到一种解决方案,以允许在后台将数据发送到 API。使用 Next.js

答:

-1赞 Huboh 1/21/2022 #1

要在 Workers 中使用 ES6 模块,您需要使用以下语法在脚本模式下加载它:

new Worker('worker-path.js', {type: 'module'})

评论

1赞 Th0rgal 1/22/2022
感谢您的回答,不幸的是这没有帮助。这是我的新代码: const worker = new Worker(new URL('./world.worker.js', import.meta.url), {type: 'module'});
9赞 infamed 5/2/2022 #2

我终于想通了,我遇到了同样的问题。最大的问题是您不应该在 Web Worker 中使用 export default。这是我如何让它工作。

您需要使用 React.useMemo 实例化工作线程。这样,只创建一个工作线程。

const worker = React.useMemo(() => new Worker(new URL('./world.worker.js', import.meta.url)), []);

若要获取响应,需要知道工作器对象何时更新。为此,您可以使用 React.useEffect。

React.useEffect(() => {
  worker.onmessage = ({ data: { answer } }) => {
    console.log(answer);
    return () => worker.terminate();
  }
}, [worker]);

最后,您的 world.worker.js 文件有两个问题。第一个是它实际上没有返回任何东西。您需要添加对 postMessage 的调用。第二个问题是我难以弄清楚的问题,是您不应该将函数包装在导出默认值中。我认为这是因为 webpack 会为你包装它。有关正确的 world.worker.js 代码,请参见下文。

import { Delaunay } from "d3-delaunay";

self.onmessage = ({ data: { question } }) => {
  postMessage({
    answer: 42,
  });
};

评论

0赞 Shannon Cole 5/3/2022
谢谢你。这对你来说仍然有效吗,你有没有发现任何其他问题?我希望使用 WebWorkers 来处理处理在用户提交后在后台将大量数据发送到 API 的处理。使用 Next.js
0赞 infamed 5/3/2022
这目前在 Webpack 5 上对我有用,并且没有任何问题。不过,与 React 相比,不确定 Next.js 的语法。