脚本应放在前最简单可靠,因dom已构建完毕;若需放,优先用defer(仅对外部脚本有效)或type="module"(默认defer行为),避免阻塞渲染。

脚本放 里会阻塞页面渲染
浏览器解析 HTML 是从上到下流式进行的,遇到 <script></script> 标签(尤其是没有 async 或 defer 的)就会暂停 DOM 构建,去下载、解析、执行 JS。这意味着即使页面只有几行 HTML,只要头部有个 200KB 的 bundle.js,用户看到白屏的时间就可能长达几百毫秒甚至更久。
常见现象:DOMContentLoaded 延迟、首屏内容长时间不出现、Lighthouse 报“Render-blocking resources”。
除非你明确需要在 DOM 构建前运行脚本(比如某些 CSS-in-JS 初始化、同步 polyfill 注入),否则别把常规业务脚本放在 里。
defer 脚本可以安全放在 ,但执行顺序有约束
defer 让脚本下载不阻塞 HTML 解析,且保证按出现顺序执行,且在 DOMContentLoaded 之前完成。它适合依赖彼此执行顺序的模块化脚本(如先加载 utils.js,再加载 app.js)。
注意点:
-
defer只对**外部脚本**有效(<script src="..." defer></script>),内联脚本加defer会被忽略 - 多个
defer脚本的执行顺序严格按它们在 HTML 中的书写顺序,和下载完成时间无关 - 如果脚本里直接操作
document.body,仍可能出错——因为defer不保证 DOM 已完全就绪(虽然通常已接近完成),稳妥做法仍是监听DOMContentLoaded
绝大多数情况,直接把 <script></script> 放在 前最简单可靠
这是兼容性最好、行为最可预测的方式:DOM 已构建完毕,所有元素都可访问,无需额外等待或判断时机。
适用场景:
- 初始化组件(如
new App().mount('#app')) - 绑定事件(
document.getElementById('btn').addEventListener(...)) - 不需要 SSR 或复杂资源调度的静态站点
示例:
<div id="app"></div> <script src="main.js"></script>
注意:main.js 里可以直接用 document.getElementById('app'),不会返回 null。
现代项目用 type="module" 时,默认行为类似 defer
ES 模块脚本(<script type="module" src="app.js"></script>)天然具有 defer 行为:非阻塞、按序执行、在 DOM 构建完成后、DOMContentLoaded 前执行。
但要注意:
- 模块脚本不支持
defer属性(加了也无效) - 模块默认是严格模式,且顶层
this是undefined - 如果模块里动态
import()一个大文件,仍可能造成交互延迟,这时得结合loading="lazy"或代码分割来优化
真正容易被忽略的是:脚本位置只是优化起点,不是银弹。如果脚本本身执行耗时(比如大量同步计算、未分片的 DOM 遍历),哪怕放尾部也会卡住主线程。这时候得看函数内部怎么写,而不是只挪标签位置。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











