为何模块中给window赋值函数无效?如何供非模块脚本调用?
问题
场景代码
module.mjs
console.log('module1 loaded'); window.module1_function = () => { console.log('module1_function executed'); return 42; }
index.html
<script defer type="module" src="./js/module1.mjs"></script> <script defer> module1_function(); </script>
执行结果
Uncaught ReferenceError: module1_function is not defined module1 loaded
可见调用失败,函数未定义,模块在函数调用后才加载;但在控制台执行module1_function()却能成功运行。
疑问
defer本应让内联脚本等待module.mjs执行完成后再处理,此时函数已赋值应可调用,为何报错?
需求
如何让模块中的该函数对普通脚本可用,且无需将普通脚本转为模块并导入函数?
解答
报错原因
带type="module"的脚本和普通脚本的defer执行逻辑存在差异:
- 普通
defer脚本会按HTML中的顺序,在DOM解析完成后、DOMContentLoaded事件触发前依次执行。 - 模块脚本(即便加了
defer)的执行时机是所有普通defer脚本执行完毕之后,DOMContentLoaded事件触发之前。
所以你的代码中,普通内联defer脚本先执行,此时模块脚本还未运行,window.module1_function尚未被定义,因此抛出引用错误。而控制台执行时页面已完全加载,模块脚本早已执行完毕,自然能找到该函数。
解决方案
方法1:监听DOMContentLoaded事件
将普通脚本的函数调用放在DOMContentLoaded事件回调中,模块脚本会在该事件触发前执行完毕:
<script defer type="module" src="./js/module1.mjs"></script> <script> document.addEventListener('DOMContentLoaded', () => { module1_function(); }); </script>
这里可去掉普通脚本的defer属性,事件回调本身会等待DOM加载完成后触发。
方法2:调整脚本顺序并移除普通脚本的defer
将普通内联脚本放在body末尾,同时移除它的defer属性。模块脚本默认延迟执行(等效于defer),会在DOM解析完成后先执行,随后执行末尾的普通脚本:
<body> <!-- 页面内容 --> <script type="module" src="./js/module1.mjs"></script> <script> module1_function(); </script> </body>
内容的提问来源于stack exchange,提问作者Neutrino
相关产品推荐
相关产品推荐

