async脚本执行时document.getelementbyid返回null,是因为其下载完立即中断html解析并执行,此时dom元素尚未创建;典型错误是中async脚本操作节点,导致“cannot read property 'addeventlistener' of null”。

async 脚本执行时 document.getElementById 为什么返回 null
因为 async 不等 DOM 解析完成——脚本下载完立刻中断 HTML 解析并执行,此时目标元素很可能还没被解析到。典型现象是 Uncaught TypeError: Cannot read property 'addEventListener' of null。
- 常见位置陷阱:
<script src="analytics.js" async></script>放在里,但脚本里写了document.getElementById('footer') - 验证方式:在脚本开头加
console.log(document.getElementById('target') === null),大概率输出true - 根本原因不是“加载慢”,而是执行时机与 DOM 构建进度完全脱钩——取决于网络下载速度,而非 HTML 解析进度
哪些脚本真正适合加 async 属性
只有满足「三不」条件的脚本才适合 async:不读 DOM、不改 DOM、不依赖其他脚本。否则就该换 defer 或移入 底部。
- 典型适用场景:
analytics.js、ads.js、错误监控 SDK(如 Sentry 的初始化代码) - 典型误用场景:
clipboard.min.js(需绑定按钮)、lodash.js(后续脚本要调_.debounce)、Vue/React 入口文件(必须挂载#app) - 第三方库文档没明确写“支持异步加载”,就别加
async——很多 SDK 内部隐式依赖performance.getEntries()等需 DOM 就绪后才稳定的 API
async 和 defer 混用会怎样
浏览器按规范直接忽略 defer,只认 async。一旦混用,顺序保障和 DOM 就绪保证全失效。
-
<script src="a.js" defer></script>+<script src="b.js" async></script>→b.js可能在a.js执行中途插入,且a.js的defer行为被丢弃 -
defer仅对带src的外部脚本生效;<script defer>console.log(1)</script>中的defer被浏览器完全忽略 - Webpack/Vite 打包出的
main.js几乎都该用defer,除非它真的一行 DOM 操作都没有
async 脚本执行会卡页面吗
会。虽然下载不阻塞 HTML 解析,但执行时浏览器必须暂停解析、交出主线程,直到脚本运行完。大体积或长任务脚本会让用户感知卡顿,拉长 LCP。
- 性能临界点:体积 >200KB 的
async脚本容易成为渲染瓶颈,尤其在低端设备上 - 替代方案优先级:非关键逻辑 → 动态
import();轻量初始化 →setTimeout(() => { ... }, 0)延后到下一个事件循环 - 兼容性注意:
async在 IE9 及以下完全不支持;若需兼容旧企业内网环境,得 fallback 到动态创建script标签
async 本身,而是脚本内部那些没写出来的依赖——比如某个统计 SDK 看似独立,实则调用了需 DOM 就绪后才可用的 API。前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











