当async和defer同时出现在一个script标签中,浏览器会严格忽略defer,按纯async规则执行:下载完立即执行,不保证顺序,也不等待dom就绪。

async 和 defer 同时写在同一个 <script></script> 标签里,浏览器直接忽略 defer
只要 <script src="a.js" async defer></script> 这种写法出现,defer 就彻底失效,浏览器按纯 async 规则处理:脚本下载完立刻执行,不保证顺序,也不等 DOM 构建完成。
这不是“优先级高低”的问题,而是规范层面的明确退让——HTML 标准规定,当 async 存在时,defer 必须被忽略。所有主流浏览器(Chrome、Firefox、Safari、Edge)均严格遵循此规则,且不报错、不警告,静默降级。
常见错误现象:
-
document.getElementById("app")在该脚本中仍可能返回null(因为执行时机不可控) - 多个同时带
async defer的脚本,执行顺序与书写顺序无关,依赖关系极易断裂 - 开发者误以为“加了 defer 就安全”,结果线上偶发 DOM 访问失败,排查困难
为什么不能靠 defer “兜底” async 的不确定性
async 的设计目标就是“尽快执行”,它主动放弃对执行时机的控制权;而 defer 的核心价值恰恰是“可控时机 + 顺序保障”。二者语义冲突,无法叠加生效。
使用场景差异很实际:
- 用
async:统计脚本(如 GA、神策)、广告代码——独立、无 DOM 依赖、越早上报越好 - 用
defer:Vue/React 初始化、表单校验逻辑、页面交互绑定——必须等 DOM 就绪,且常依赖前置脚本(如工具库) - 混用同一标签:没有合理场景。要么你真需要立即执行,那就只留
async;要么你需要稳态执行,那就只留defer
defer 属性本身还有两个硬性前提条件
defer 不是写了就生效,它只在同时满足以下两个条件时才起作用:
-
src属性必须存在(即只能用于外部脚本,<script defer>console.log(1)</script>中的defer会被忽略) -
defer必须作为无值布尔属性出现(defer="true"或defer="defer"均无效)
这两个限制和 async/defer 共存问题一样,都是“静默失效”:浏览器既不报错,也不提示,脚本照常加载执行,只是行为回归默认同步模式——这正是最容易被忽略的坑。
真实项目中怎么避免踩坑
检查构建产物或上线 HTML 时,重点关注三类写法:
-
<script async defer src="x.js"></script>→ 立即删掉defer -
<script defer src="x.js"></script>→ 改成<script defer src="x.js"></script> -
<script defer>...</script>(内联脚本)→ 改成外链,或干脆不用defer
如果项目用了构建工具(Webpack/Vite),建议在 HTML 插件配置中加一层 lint:正则匹配 <script>]*(async|defer)[^>]*(async|defer)[^>]*></script> 并告警。这类问题往往在线上灰度阶段才暴露,修复成本远高于预防。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











