模块类型 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 中多个脚本的高效动态导入的主要内容,如果未能解决你的问题,请参考以下文章