脚本应放底部或中加defer属性,避免因dom未加载导致getelementbyid返回null;async不保证执行顺序,不适合dom操作;内联脚本不支持defer/async,模块脚本默认等同defer。

script 标签放 还是 ,取决于它要不要访问 DOM 元素、是否依赖页面结构,以及你愿不愿意承担阻塞渲染的风险——不是“哪个更好”,而是“哪个更合适”。
脚本执行时报 document.getElementById(...) is null 怎么办
这是最典型的放置错误信号:脚本在 DOM 元素还没解析出来时就执行了。
- 如果
<script></script>在里且没加defer或async,浏览器会暂停 HTML 解析去下载并执行它,此时内容根本还没开始解析,getElementById必然返回null - 把脚本移到
前(即最底部)是最直接的解法,确保所有元素已挂载 - 若必须保留在
,加defer属性即可:<script src="app.js" defer></script>—— 它会异步下载,但严格按顺序、在 DOM 解析完成之后执行 - 别用
async替代defer来解决这个问题:async下载完就执行,时机不可控,仍可能早于 DOM 构建完成
defer 和 async 的实际行为差异
两者都让脚本不阻塞 HTML 解析,但执行时机完全不同,选错会导致依赖错乱或 DOM 访问失败。
-
defer:脚本下载与 HTML 解析并行,但执行被推迟到整个文档解析完毕(DOMContentLoaded之前),且多个defer脚本按出现顺序执行 -
async:脚本下载完立即执行,完全不等待其他脚本或 DOM;适合独立工具类(如统计埋点、广告 SDK),不适合操作 DOM 或依赖其他 JS 的逻辑 - 没有
src的内联脚本(如<script>console.log('hi')</script>)不支持defer或async,只能靠位置控制执行时机 - 现代项目中,打包工具(如 Webpack/Vite)生成的入口脚本建议统一用
defer,既保顺序又不阻塞渲染
第三方脚本(如 Google Analytics、微信 JS-SDK)该放哪
这类脚本通常不要求访问当前页面 DOM,但对加载速度敏感,且多数自身已处理好执行时机。
- 优先使用
async放在:避免阻塞,又能让它尽早开始下载(比如分析脚本越早埋点越准) - 确认文档是否要求特定加载方式:微信 JS-SDK 明确要求在
开头加载,否则config失败;而 Sentry 推荐async+ - 如果第三方脚本内部调用了
document.write(极少见但存在),它只能放在或解析过程中,放到底部反而会清空整个页面 —— 遇到这种脚本,先查文档,别硬套规则 - 用
async时注意:它不保证执行顺序,若多个第三方脚本有依赖(比如 A 初始化后 B 才能调用),就得合并或手动加加载逻辑
真正容易被忽略的是内联脚本和模块脚本(type="module")的行为差异:type="module" 默认表现等同于 defer,哪怕写在 里也不会阻塞;而传统脚本没有这个默认保障。混用时尤其要注意执行时序,别以为加了 module 就万事大吉。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











