导入含异步数据的模块后,DOMContentLoaded与load事件未触发
问题:导入含顶级await的模块后,DOMContentLoaded/load事件监听失效
问题原因
- ES模块默认异步加载,再加上你的
test.js里用了顶级await(let test = await f();),模块执行会等待异步请求完成才继续。 - 等模块执行到添加事件监听的代码时,DOMContentLoaded甚至load事件可能已经触发完了——浏览器在加载模块的这段时间里,DOM可能早就解析完成,页面资源也加载好了,事件触发过一次后,后续再添加监听就不会触发回调了。
- 而jQuery的
$(document).ready()能正常工作,是因为它内部会先检查DOM是否已经就绪,如果已经就绪,会直接执行回调,不是只靠事件监听。
解决方案
方案1:先加事件监听,再导入模块
把事件监听代码放在import语句前面,确保在DOM事件触发前就完成监听:
<head> <script type="module"> // 先注册事件监听 window.addEventListener("DOMContentLoaded", () => { console.log("DOMContentLoaded"); }); window.addEventListener("load", () => { console.log("load"); }); // 再导入模块 import test from "/js/test.js"; console.log(test); $(document).ready(() => { console.log("ready"); }); </script> </head>
方案2:主动检查事件是否已触发
通过document.readyState判断当前DOM状态,如果事件已经触发过,就直接执行回调,否则再添加监听:
// 处理DOMContentLoaded if (document.readyState === "interactive" || document.readyState === "complete") { console.log("DOMContentLoaded"); } else { window.addEventListener("DOMContentLoaded", () => { console.log("DOMContentLoaded"); }); } // 处理load事件 if (document.readyState === "complete") { console.log("load"); } else { window.addEventListener("load", () => { console.log("load"); }); }
方案3:修改模块逻辑,移除顶级await
把test.js里的顶级await改成导出Promise,在主模块里等待这个Promise,同时提前设置事件监听:
// 修改后的test.js async function f() { const r = await fetch("url", { method: "post", body: JSON.stringify({ "key": "value" }), headers: { "Content-Type": "application/json", "Accept": "application/json", } }) .then(res => res.json()) .then(result => result); return r; } export default f(); // 导出一个Promise
<!-- 修改后的main.js --> <head> <script type="module"> // 先注册事件监听 window.addEventListener("DOMContentLoaded", () => { console.log("DOMContentLoaded"); }); window.addEventListener("load", () => { console.log("load"); }); import testPromise from "/js/test.js"; // 等待Promise完成后处理数据 testPromise.then(test => { console.log(test); }); $(document).ready(() => { console.log("ready"); }); </script> </head>
内容的提问来源于stack exchange,提问作者D L
相关产品推荐
相关产品推荐

