DOMContentLoaded 永远不会在模块内触发

Fer*_*Fer 5 javascript ecmascript-6 es6-modules

我对在特定 ES6 模块加载场景中未触发的事件感到困惑。这是一个支持我的示例的 Codepen 项目: https://codepen.io/fchristant/project/editor/AYQkGJ

考虑这个非常简单的模块app.js

console.log('in module');
document.addEventListener('DOMContentLoaded', (event) => {
  console.log('DCL:2');
});
Run Code Online (Sandbox Code Playgroud)

我正在使用来自文档头部脚本标签的动态 ES6 导入:

<script>
  (async () => {
    const module = await import("./js/app.js");
    console.log('async done');
   })();
</script>
Run Code Online (Sandbox Code Playgroud)

所以,这个调用代码是一个非模块(同步脚本)动态加载模块。因为它位于文档的头部,而不是异步或延迟,所以我希望它立即运行,并且以阻塞方式执行。

这是我的期望,但不是正在发生的事情。特别是,问题是模块中的事件永远不会被触发。而且看起来是因为现在听已经太晚了,事件已经发生了。考虑到文档头部的阻塞脚本,这怎么可能?它看起来是非阻塞的,但我不明白为什么。

需要明确的是,讨论的重点不是这是否是一个好主意,我首先感兴趣的是为什么这不起作用的概念性答案。或者换句话说:非模块脚本如何以同步方式动态加载模块?

Emi*_*ier 5

等待文件domcontentloaded中发生该事件index.html。在事件的回调中导入您想要在事件触发时运行的模块。

// app.js
export function start() {
  console.log('in module');
  console.log('DCL:2');
}
Run Code Online (Sandbox Code Playgroud)
<!-- index.html -->
<script>
  document.addEventListener('DOMContentLoaded', async (event) => {
    const module = await import("./js/app.js");
    module.start();
    console.log('async done');
  });
</script>
Run Code Online (Sandbox Code Playgroud)

或者,如果您不需要任何模块,而只是希望在渲染 DOM 之前将所有内容逐一加载,则只需使用传统的渲染阻塞<script>标签即可。

<script src="./js/app.js"></script>
<script>
  console.log('module loaded');
</script>
Run Code Online (Sandbox Code Playgroud)

  • 考虑一下,您的模块是通过动态导入“异步”加载到文档中的,没有办法解决这个问题。因此,如果您的 DOM 很大并且连接速度很快,那么您可能会及时捕获“domcontentloaded”事件。但由于 DOM 较小且连接速度较慢,您的模块可能总是为时已晚。所以你有一个竞争条件。所以是的,这是不可能的。 (5认同)
  • 不,模块处理像“domcontentloaded”这样的事件并不奇怪,至少当您使用将所有内容都放在单个文件中的模块捆绑器时。 (2认同)
  • 就我而言,app.js 本身就是一个模块,其原因是它静态导入自己的模块。仅当 app.js 也是一个模块时,才可以进行静态导入。然而似乎没有办法同步加载任何模块,这看起来根本不可能。看来你是对的。 (2认同)