模块类型 Web Worker 中多个脚本的高效动态导入

Posted

技术标签:

【中文标题】模块类型 Web Worker 中多个脚本的高效动态导入【英文标题】:Efficient dynamic import of multiple scripts in module type Web Worker 【发布时间】:2020-11-11 01:05:23 【问题描述】:

我有一个需要在启动时动态加载多个脚本的网络应用程序。为了提高效率,它需要通过网络并行请求所有脚本。为了正确起见,脚本必须按特定顺序执行。

在 DOM 中,这可以通过动态添加 <script> 标签并将 async 属性设置为 false 来实现。请求并行发出,脚本按照添加标签的顺序运行。

在经典类型的Web Worker(默认)中,可以使用importScripts(...arr)。浏览器一次性获取所有脚本的列表,因此可以同时请求它们,但要保证脚本按指定的顺序运行。

在模块类型的 Web Worker 中,事情变得更加棘手。 importScripts 不允许在模块类型工作者中使用(如果您尝试,Chrome 会抛出 Module 脚本不支持 importScripts())。工人不能添加脚本标签。但是,您可以使用动态 import() - 但它只接受一个脚本。如果您像这样一次导入一个脚本:

async LoadScripts(scriptsArr)

    for (const src of scriptsArr)
        await import(src);

这样会序列化网络请求一个接一个地运行,效率低。

我唯一能想到的就是动态创建一个仅包含 import '...'; 行的模块,然后动态导入它,如下所示:

async LoadScripts(scriptsArr)

    // Create a string with a module full of import statements
    const moduleStr = scriptsArr.map(src => `import '$src';`).join("\n");

    // Create a blob from the string
    const blob = new Blob([moduleStr],  type: "application/javascript" );

    // Dynamic import the blob, which in turn loads all the import statements
    await import(URL.createObjectURL(blob));

这感觉就像一个非常丑陋的黑客,就像一个美化的eval()。有没有更好的办法?

【问题讨论】:

"我有一个 web 应用程序需要在启动时动态加载多个脚本" - 你能详细说明一下要求吗?为什么它是动态的,它如何决定哪些脚本需要加载,哪些不需要?如果它们以意外的顺序加载会出现什么问题? 我们在浏览器中制作了 Construct 3,一个完整的游戏开发 IDE。游戏预览在本地工作并且是模块化的,可能包括本地安装的第三方插件。所以在预览中加载的脚本没有固定的列表,依赖运行时的第三方脚本必须在运行时脚本之后,并且运行时也有自己的内部依赖项(正如我在另一条评论中提到的,我们正在从经典脚本迁移,所以还没有导入依赖链) 我认为对于没有服务器交互的预览功能,createObjectURL 是一个非常好的和合适的解决方案。然后,游戏的实际构建可能会将其生成为静态文件。 【参考方案1】:

为了正确起见,脚本必须按特定顺序执行。

如果脚本确实有依赖关系,它们应该使用 import 语句显式声明它们。

创建一个以正确顺序导入所有内容的单个模块,就像您动态构建的模块一样,也是一种选择,可能模块列表实际上是相当静态的。

如果一次导入一个脚本,会序列化网络请求一个接一个运行,效率低。

如果模块声明了它们的依赖关系,您可以一次加载它们:

function loadScripts(scriptsArr) 
    return Promise.all(scriptsArr.map(src => import(src));

【讨论】:

我们正在从经典脚本迁移,因此我们使用“仅用于副作用的导入”语法,即import 'foo.js'; @AshleysBrain 但是,如果您不使用模块功能,为什么?以及如何处理导出,将所有内容放在全局范围内?

以上是关于模块类型 Web Worker 中多个脚本的高效动态导入的主要内容,如果未能解决你的问题,请参考以下文章

Nginx为什么高效?一文搞明白Nginx核心原理

Web Worker 案例

Web Worker 中的 Emscripten WASM:“模块不是对象或函数”

使用 GWT 的 Web Worker

linux mpm模式用prefork好还是worker好

html5之Web Worker -- js多线程编程