defer属性仅对外部脚本(含src)生效,内联脚本加defer会被浏览器忽略;其执行时机在dom构建完毕后、domcontentloaded事件触发前,且多个defer脚本严格按html中声明顺序执行。

defer 属性只对外部脚本生效
内联 <script>console.log(1)</script> 加 defer 完全无效,浏览器会忽略该属性。只有带 src 的 <script src="..." defer></script> 才触发延迟解析和执行逻辑。
这是因为 defer 的设计目标是“让外部脚本不阻塞 HTML 解析,但保持执行顺序”,而内联脚本天然已嵌入文档流,无需也不支持延迟加载。
- 错误写法:
<script defer>alert('hi')</script>——defer被静默丢弃 - 正确写法:
<script src="app.js" defer></script> - 若需延迟执行内联逻辑,改用
DOMContentLoaded或setTimeout等手动控制
多个 defer 脚本按出现顺序执行
这是 defer 和 async 的关键区别:所有带 defer 的外部脚本,无论下载快慢,都会等 HTML 解析完成、按 <script></script> 标签在文档中的书写顺序依次执行。
比如:<script src="a.js" defer></script> 在前,<script src="b.js" defer></script> 在后,即使 b.js 先下载完,也必须等 a.js 执行完再执行 b.js。
- 适合有依赖关系的模块,如
lodash.js→utils.js→main.js - 不适用于想尽早执行、且无依赖的独立脚本(此时用
async更合适) - 注意:这个顺序保证仅限于同为
defer的脚本;混用async或无属性脚本会打破顺序
defer 不等于 document.addEventListener('DOMContentLoaded', ...)
defer 脚本的执行时机是“HTML 解析完成之后、DOMContentLoaded 事件触发之前”,它本身就会触发该事件(前提是它是最后一个推迟执行的脚本)。但两者语义不同:
-
defer是资源加载策略,影响的是脚本何时下载、何时执行 -
DOMContentLoaded是事件机制,不管脚本怎么加载,只要 DOM 构建完毕就触发 - 如果页面只有
defer脚本,且它们执行很快,DOMContentLoaded几乎紧随其后;但如果某个defer脚本里写了死循环或长时间同步操作,会拖慢该事件触发
IE10+ 支持 defer,但不支持 module + defer 组合
现代浏览器中 <script type="module" src="x.js" defer></script> 是合法且推荐的写法,模块默认具有 defer 行为;但旧版 IE 完全不识别 type="module",直接跳过该脚本。
更隐蔽的问题是:某些构建工具(如 Webpack)输出的模块化代码,在未显式加 defer 时,若被插入到 中,仍可能阻塞解析 —— 因为模块虽自带异步加载,但不自动具备 defer 的执行时序保证。
- 兼容性底线:要支持 IE11,避免用
type="module";用传统<script src="..." defer></script> - 现代项目中,
type="module"已隐含defer效果,显式加defer不报错但冗余 - 真正容易被忽略的是:服务端渲染(SSR)场景下,动态注入的
<script></script>标签即使带defer,也不会生效 —— 只有初始 HTML 中的<script></script>才受解析器识别
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











