Web Workers - 如何导入模块

75

我正在使用 ES2015 的导入/导出模块。

在我的 worker 文件中,当我尝试像平常一样导入函数时:

worker.js

import { a, b, c } from "./abc.js";

我遇到了错误:SyntaxError: import declarations may only appear at top level of a module

由于我在模块'abc.js'中导出函数,我不确定如何使用旧的(似乎已经过时)语法来使用它们:

self.importScripts( "/app/abc.js" );

我的问题是,我们如何在工作线程中使用新的导入模块语法?

第二个问题是,当从没有导出全局对象父级的模块中导入时,importScripts 导入到哪里?

5个回答

95

ES2015模块在Workers中在Safari和Chromium浏览器中可用。

如果其他浏览器/版本是您的目标,您仍然需要使用importScripts()

当可用时,您可以像这样创建一个模块化的worker:

new Worker("worker.js", { type: "module" });

请参阅:https://html.spec.whatwg.org/multipage/workers.html#module-worker-example 以下是每个浏览器的错误报告: 相关的"Can I Use"实时兼容性数据: https://caniuse.com/?search=module%20worker

3
如何知道何时会得到支持? - Yairopro
1
请查看我的更新答案。Chromium正在积极开发它。 - Tobias Buschor
啊,所以只有新的Worker(..)需要指定“module”。实际的worker代码可以像正常情况下一样使用import/export。这是正确的吗?听起来像是Node的.mjs :) - backspaces
该死, SharedWorker 的82版本怎么回事?好吧,他们决定跳过这个版本了,而83的稳定版计划于2020年5月19日发布。 - Константин Ван
将工作线程的代码分解为多个模块,同时保持其作为单个模块的状态,这种做法可行吗?我认为上述已经是可以了。一旦定义了一个 Web Worker 模块,您就可以在其中 import export 其他模块,否则可以使用 importScripts 进行相同操作。因此,单个 Web Worker 可以成为一个多模块模块,对吧? - sçuçu
显示剩余2条评论

11

https://twitter.com/DasSurma/status/1392799265567694851 - Ayman Morsy

3

Chrome已经支持在Web Worker中使用ES模块,启用实验性的Web平台功能只需要在启动Chrome时使用正确的标志即可:

chrome.exe --enable-experimental-web-platform-features

以下是您需要使用的语法将工作线程脚本加载为模块:

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

这一功能已经开发了近一年,将很快面世,无需特殊标志,但目前还没有官方发布日期。


此外,不需要从标志开始,可以在 chrome://flags/ 中搜索 实验性 Web 平台功能 或类似内容。 - Fox

3
截至11月21日,将模块导入工作程序仍然存在不稳定性。一种解决方案是使用rollup从您的工作程序生成IIFE,具体操作如下:
//worker.js
import { MyModule } from 'my-module.js'
onconnect = async (e) => {
    var port = e.ports[0];
    MyModule.func()

    port.onmessage = (e) => {
        port.postMessage("Hi App");
    }
}

//rollup config
export default [
{
        'input': 'worker.js',
        'output': {
            'file': 'dist/worker.js',
            'format': 'iife'
        },
 },
]


//dist/worker.js (rollup output)

(function () {
    'use strict';
    //MyModule code here, generated by rollup
    
    MyModule.func()
    onconnect = async (e) => {
        var port = e.ports[0];
        port.onmessage = (e) => {
            port.postMessage("Hi App");
        };
    };

}());

//main app

const worker = new SharedWorker("/dist/worker.js");
worker.port.onmessage = (e) => {
    console.log('Message received from worker: ' + e.data);
}
worker.port.postMessage("Hi worker");

实际上,Rollup正在执行浏览器应该执行的工作。

这对我来说很有效。当然,模块代码也会被复制到worker中,因此代码大小会增加。但仍然保持DRY,因为代码是由Rollup生成的。


-3
对我来说,分配给self.很管用。我已经把导入放到另一个js文件中:abcImported.js
import { a, b, c } from "./abc.js";

export {  a, b, c };

在 Service Worker 中:
self.a = require('abcImported.js').a;
self.b = require('abcImported.js').b;

这样,它就可以在 worker 内部访问。(在 Chrome 中测试过)


1
你从哪里获取 require 的? - Jefferey Cave
我认为这是另一个(疯狂的!)案例,工作流已经将“require”修改成了可以解决问题的解决方案。 Rollup?或更可能是Webpack? - backspaces
没错,webpack。在我的情况下,是使用了 webpacker gem 的 Rails 应用程序,它使用了 webpack :) - Kate Kasinskaya
abcImported.js 是一个单独托管的文件吗? - arctic_hen7

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接