defer脚本执行顺序严格按html中标签出现顺序,仅对带src的外部脚本生效;内联脚本、空src或赋值defer均无效;type="module"天然defer,勿混用;async与defer共存时defer失效;defer不保证资源加载完成。

多个 defer 脚本的执行顺序由 HTML 书写位置决定
浏览器不看谁下载快、文件大小或网络延迟,只认 <script></script> 标签在 HTML 中出现的先后。哪怕 app.js 比 vendor.js 先下完,也必须等 vendor.js 执行完才轮到它。
常见错误是把依赖方写在前面:<script src="app.js" defer></script> 放在 <script src="utils.js" defer></script> 前面 → 运行时报 ReferenceError: utils is not defined。
构建工具(如 Webpack、Vite)可能自动往 插入 polyfill 或 runtime 脚本,无意中把它们放在你手动写的脚本前面,破坏原有顺序。
defer 只对带 src 的外部脚本生效
以下写法全部无效,浏览器静默退回到默认行为(同步执行或乱序):
-
<script defer>console.log(1)</script>—— 内联脚本,defer被忽略 -
<script src="" defer></script>或<script src="" defer></script>——src为空或空格,等同于阻塞加载 -
<script src="a.js" defer></script>——defer是布尔属性,赋值无意义,语义混乱
真正起作用的只有两个硬条件:src 存在 + defer 以无值形式出现(即 defer 单独写,不带等号和值)。
defer 和 type="module" 别混用
<script type="module" src="main.js"></script> 天然具备 defer 行为:并行下载、DOM 就绪后执行、严格按声明顺序运行。再加 defer 属于冗余,部分构建工具会因此误判依赖关系。
注意两点:
-
type="module"脚本默认启用严格模式,且支持顶层await,迁移时得验证初始化逻辑是否兼容 - 它和普通
defer脚本不在同一执行队列里:如果 HTML 里同时有<script src="legacy.js" defer></script>和<script type="module" src="new.js"></script>,两者执行时机接近但不严格同步,别让它们互相假设对方已执行完毕
async 和 defer 混用会直接破坏顺序保证
只要一个 <script></script> 同时写了 async 和 defer,浏览器会完全忽略 defer,按 async 规则处理:谁先下载完谁先执行,顺序不保证。
例如:<script src="utils.js" async defer></script> 等价于只写 async。
动态插入的脚本(如 document.createElement('script'))默认带 async 行为,即使你手动设 script.defer = true,老浏览器也不认,别赌兼容性。
真正容易被忽略的是:defer 保证 DOM 就绪,但不保证资源就绪 —— 比如 canvas 元素已存在,但图片还没加载完;或者脚本依赖的第三方 API 尚未响应。这些得靠运行时判断,不是 defer 能解决的。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











