动态插入的script元素即使设defer=true也不保序,因浏览器仅对静态html中的defer属性调度,动态脚本默认按async处理;须用promise链或onload回调显式串行控制依赖。

动态插入的<script>设defer=true,不保证顺序执行</script>
不会。动态创建的 <script></script> 元素(比如用 document.createElement('script'))即使显式设置 script.defer = true,在多数浏览器中也**不被识别为 defer 行为**,更不保序。
原因很简单:HTML 解析器只对静态 HTML 中的 defer 属性做调度;而 JS 动态插入的脚本,浏览器默认按 async = true 处理——下载完立刻执行,谁快谁先跑。
- IE 和旧版 Safari(
Safari )完全忽略动态脚本上的 <code>defer - Chrome 和现代 Firefox 虽支持
script.defer = true,但不保证多个动态脚本之间的执行顺序 - 即使你手动控制插入顺序:
a.js插入后才插b.js,也不能确保b.js在a.js执行完后再运行
为什么不能靠 defer 解决动态脚本依赖
动态脚本的加载和执行脱离了 HTML 文档解析上下文,也就失去了 defer 的核心保障:DOM 就绪 + 声明顺序排队。
常见错误现象:loadScript('vendor.js'); 后紧跟 loadScript('app.js');,结果 app.js 先执行、报 ReferenceError: $ is not defined。
-
defer的“保序”只对静态<script defer></script>标签有效,是 HTML 解析器内置机制 - 动态脚本没有“文档声明顺序”,浏览器只认下载完成时间,不是插入 DOM 的时间
- 想让
b.js等a.js执行完再跑?必须用Promise链或onload回调显式串行
替代方案:用 Promise 控制动态脚本加载顺序
真正可控的方式是封装加载逻辑,而不是依赖属性。
例如:
function loadScript(src) {
return new Promise((resolve, reject) => {
const s = document.createElement('script');
s.src = src;
s.onload = resolve;
s.onerror = reject;
document.head.appendChild(s);
});
}
// 顺序执行
loadScript('vendor.js')
.then(() => loadScript('app.js'))
.then(() => initApp());
- 避免用
document.write()或innerHTML += <script></script>插入脚本——该方式会触发同步阻塞且defer失效 - 不要设
script.async = false试图模拟同步——这会让后续 HTML 解析卡住,得不偿失 - 如果脚本本身是 ES 模块,优先用
import()动态导入:await import('./vendor.js');,天然支持依赖链和错误捕获
容易被忽略的关键点
很多人以为“只要写了 defer,就等于安全排队”,但这个前提只成立在静态 HTML 中。
动态插入时,连 DOMContentLoaded 都不能作为可靠锚点——因为动态脚本可能在事件触发前、中、后任意时刻执行。
最稳妥的做法是:把依赖关系写进代码逻辑里,而不是指望浏览器替你排好队。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











