defer仅对带src的外部脚本生效,执行时机固定在DOM构建完成、DOMContentLoaded之前,按HTML顺序执行,不支持内联脚本,混用async会忽略defer,且无法拦截运行时错误。
defer 不是“加了就自动延迟执行”的万能开关,它只对带 src 的外部脚本生效,且执行时机严格固定在 DOM 构建完成、DOMContentLoaded 之前——这意味着你能在脚本里直接操作 document.body 或 document.getElementById,但不能指望它等图片或 CSS 加载完。
src)有效浏览器会完全忽略内联脚本上的 defer 属性,既不报错也不延迟:
<script defer>init();</script> —— init() 会在 HTML 解析到此处时立即执行(等同于无属性)<script src="main.js" defer></script> —— 下载异步,执行推迟到 DOM 就绪后<script src="utils.js" defer="false"></script> —— defer 是布尔属性,赋值会被忽略,实际仍生效执行顺序保序 ≠ 加载顺序保序,更不等于逻辑依赖自动满足。如果 app.js 依赖 lodash.js,但 HTML 中 app.js 写在前面,就会报 ReferenceError: _ is not defined:
<script src="lodash.js" defer></script> → <script src="app.js" defer></script>
<script src="app.js" defer></script> → <script src="lodash.js" defer></script>
async 和 defer:浏览器以 async 为准,defer 被静默丢弃,执行可能穿插进 defer 队列中间document.write
哪怕 DOM 已就绪,document.write 在 defer 脚本中也会被浏览器直接忽略,或触发严重错误(如清空整个页面),现代代码基本不用,但老项目迁移时容易踩坑:
立即学习“前端免费学习笔记(深入)”;
<script src="legacy.js" defer></script> 中含 document.write('<div>...</div>') → 页面异常或控制台报错document.body.insertAdjacentHTML 或 document.createElement + appendChild
window.__INITIAL_STATE__)必须放在所有 defer 脚本之前,且自身不能加 defer,否则 defer 脚本读不到真正容易被忽略的是:defer 只管“什么时候执行”,不管“执行成不成”。路径 404、CSP 阻断、模块内部动态 import() 失败,都不会被 defer 拦住或重试——这些得靠运行时错误捕获和 fallback 逻辑兜底。