优先选 defer,仅当脚本完全自包含且不读写 DOM、不依赖其他脚本时才用 async;defer 仅对带 src 的外部脚本生效,内联脚本加 defer 无效。

直接结论:用 async 还是 defer,不看“要不要延迟”,只盯住两个事实:脚本是否读取或修改 DOM,以及是否依赖其他脚本的执行结果。
async 脚本里调 document.getElementById 报 null 怎么办
这是最典型的 async 误用现象——脚本下载完就立刻执行,此时 DOM 可能还没解析到目标元素。
- 确认该脚本是否真的不需要 DOM:比如埋点、广告 SDK、错误监控等“自包含”逻辑才适合
async - 如果脚本里有
document.querySelector、addEventListener或操作body,一律禁用async - 第三方库如
clipboard.min.js文档没明确写“支持异步加载”,就别加async;lodash.js加了async后,紧跟着用_.debounce很可能报ReferenceError
多个 defer 脚本为什么还是执行错乱
defer 保证执行顺序,但前提是它们都出现在同一份 HTML 中、且没混入 async 脚本。
文章转信息图。将文章/笔记转化为手机可读的 HTML 信息图,自动匹配视觉风格。触发场景:文章转图、笔记转图、信息图、转小红书图、做张图、可视化这篇文章、文生图。
-
defer只对带src的外部脚本生效;内联<script defer>console.log(1)</script>的defer被浏览器忽略 - 页面里同时存在
<script defer src="a.js"></script>和<script async src="b.js"></script>,b.js可能在a.js执行中途插入运行,顺序彻底失控 - Webpack/Vite 打包出的
main.js,只要没做资源预加载,就该用defer;SSR 页面中,window.__INITIAL_STATE__注入必须在所有defer脚本之前,且不能加defer
Vue/React 入口脚本该用 defer 还是 async
绝大多数情况下选 defer。现代框架入口通常要挂载到 #app、读取服务端注入数据、或依赖 DOM 就绪状态。
立即学习“前端免费学习笔记(深入)”;
- 用
async时,DOMContentLoaded可能还没触发,document.getElementById('app')就是null - 如果入口脚本内部用了
document.write(极少见),defer会失效甚至报错,此时只能放<body>底部同步加载 - 动态 import() 拆分的代码块不受
defer/async影响,它们由 JS 引擎按需加载,无需额外标记
最容易被忽略的一点:defer 和 async 都只作用于外部脚本;而真实项目里,常有人把初始化配置写成内联 script 再加 defer,结果白加——浏览器根本不认。


















