加了defer的script标签不阻塞html解析和渲染;它异步下载、等待文档解析完成后再按顺序执行,确保dom就绪且执行时机在domcontentloaded之前。

script标签加了defer还是阻塞渲染?先看加载时机
加了defer的<script></script>不会阻塞HTML解析,但会等整个文档解析完成后再执行——不是“一下载完就执行”,也不是“DOMContentLoaded之后才开始下载”。常见误解是以为它像async一样边下边执行,其实它严格按顺序下载、排队执行,且只对外部脚本(src存在)生效。
容易踩的坑:
- 内联脚本(没有src)加defer会被忽略,浏览器直接同步执行
- 多个defer脚本仍保持书写顺序执行,哪怕后写的脚本先下载完
- 如果页面里混用了async和defer,async可能更早执行,破坏依赖关系
哪些场景必须用defer而不是async
当脚本依赖DOM结构,又不想手动监听DOMContentLoaded时,defer是最省心的选择。比如初始化导航菜单、绑定表单事件、读取document.body尺寸等操作。
适用条件:
- 脚本有明确执行顺序要求(如utils.js必须在main.js之前运行)
- 脚本需要访问完整DOM树,但不依赖其他异步资源(如API响应)
- 你控制不了脚本内容(第三方SDK文档明确要求defer)
- 页面首屏渲染速度敏感,但脚本逻辑不能被延迟到用户交互后
defer和type=module默认行为冲突吗
不冲突,而且天然一致:type="module"脚本默认就具有defer语义——自动延迟执行、保证顺序、不阻塞解析。所以给模块脚本再写defer是冗余的,但不会报错。
注意点:
- type="module"脚本即使没src(内联),也会延迟执行,这点和普通defer不同
- 模块脚本的defer行为不可取消,无法通过移除属性恢复同步执行
- 如果混用传统脚本和模块脚本,它们的执行时机不同:普通defer脚本在DOMContentLoaded前执行,模块脚本也在同一时机,但两者队列独立
Chrome DevTools里怎么验证defer是否生效
打开Network面板,选中JS文件,看Timing标签页里的Start Time和End Time是否明显晚于HTML解析完成时间(可对比DOMContentLoaded触发时间)。更直接的方式是在脚本开头加console.log('loaded'),配合Performance面板录制,观察日志时间戳是否落在Parse HTML阶段之后、DOMContentLoaded之前。
关键信号:
- 脚本下载请求出现在HTML解析中途,但执行时机标记为deferred
- 在document.readyState为loading时脚本已下载完,但未执行
- 执行时document.body已存在,且所有元素都可查找到(不像async可能出现undefined)
最常被忽略的是:defer只解决渲染阻塞,不解决执行耗时。如果脚本本身要遍历5000个节点做样式计算,它依然会让主线程卡顿——只是卡的时机从“页面白屏时”挪到了“DOM就绪后”。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











