在我的网站上,我使用javascript模块,根据MDN的说法,这些模块默认是延迟的。我有一些额外的javascript,只有在这些模块加载并执行后才能安全执行。MDN 在这里表示,延迟脚本执行保证在触发DOMContentLoaded
事件时已经发生。此外,此处建议您还可以考虑此事件已经触发的情况,如下所示:
function doSomething() {
console.info('DOM loaded');
}
if (document.readyState === 'loading') { // Loading hasn't finished yet
document.addEventListener('DOMContentLoaded', doSomething);
} else { // `DOMContentLoaded` has already fired
doSomething();
}
上面的块似乎是我正在寻找的。但是MDN上其他地方的文档让我不确定这是否真的正确。例如,为什么上面的readystate
检查查找loading
而不是loading
或interactive
?readystate
的文档说loading
后面跟着interactive
,并且在interactive
状态下"子资源,如脚本,图像,样式表和框架仍在加载。
所以在我看来,这里存在不一致。建议的readystate
检查不足以保证DOMContentLoaded
已触发,或者DOMContentLoaded
不足以保证延迟的脚本已完成。
当readyState
至少为interactive
时,这意味着文档已被完全解析,并且源 HTML 中的所有元素现在都存在于 DOM 中。
通常,实现此解决方案的人这样做是因为他们试图将侦听器附加到元素,并且需要等待所有元素存在于 DOM 中 - 因此他们需要做的就是检查document.readyState === 'loading'
。
如果您还想等待<script type="module">
脚本运行,这是一个不同的问题,具有不同的解决方案。
到目前为止,最好的方法是在模块中为您的应用程序设置一个入口点,这样您就不必担心加载顺序 - 它会正常工作。
如果您真的必须确定所有模块脚本何时都从非模块脚本运行(我不建议这样做),则必须迭代它们并侦听它们的load
事件。
// run this at the end of the body -
// once all script tags exist, but before they've run
Promise.all(
[...document.querySelectorAll('script[type="module"]')]
.map(script => new Promise(
resolve => script.addEventListener('load', resolve)
))
)
.then(() => {
// all module scripts are loaded
})
但这非常复杂,可能不是一个好方法。
如果您改为侦听窗口的load
事件,您还将等待所有其他资源加载(图像和样式表等),这是不希望的。
import "other-module";
// module is guaranteed to be loaded here
通过导入模块,可以保证在模块导入运行之前加载它。