普通标签会让页面白屏,因为不加属性的<script>标签会阻塞HTML解析、DOM构建、样式计算和页面绘制;常见现象包括getElementById返回null、文字后显示弹窗、首屏空白等。

普通<script>标签为什么会让页面白屏</script>
不加任何属性的 <script> 标签,就是渲染阻塞的根源。浏览器一碰到它,立刻暂停 HTML 解析、冻结 DOM 构建、中止样式计算和页面绘制——哪怕脚本只有 1 行空语句,HTTP 请求本身就会触发阻塞。
常见错误现象:
-
document.getElementById("main")返回null,不是元素没写,是解析根本没走到那儿 - 文字写在
<script>alert(1)</script>前面,用户却先看到弹窗、后见文字 - 脚本放在
<head>里,首屏完全空白,TTFB + 下载耗时 > 200ms 就明显卡顿
实操建议:
- 除非是
type="module"入口,否则别把<script>放在<head>里 - 内联脚本要操作 DOM,必须放在目标元素之后,或包进
DOMContentLoaded回调 - 用 DevTools → Network 查看脚本是否成为关键路径瓶颈;若单个脚本下载耗时超 300ms,优先外链 + CDN + 缓存
defer 脚本按序执行但会拖住 DOMContentLoaded
defer 解除解析阻塞,但执行时机卡在“HTML 解析完成、DOMContentLoaded 触发前”,且严格按 HTML 中书写顺序执行——这对依赖关系友好,但也埋了隐患。
立即学习“前端免费学习笔记(深入)”;
使用场景:
- 需要访问完整 DOM 的初始化逻辑(如菜单挂载、表单校验)
- 多个有先后依赖的脚本(
utils.js必须在app.js之前)
容易踩的坑:
-
defer只对外链脚本有效,<script defer>console.log(1)</script>的defer被忽略 - 多个
defer脚本跨<body>拆开(比如一个在<head>,一个在<body>底部),顺序可能错乱 - 某个
defer脚本加载极慢(如 CDN 故障或网络抖动),DOMContentLoaded会被卡住,影响所有后续监听器
async 脚本谁先下完谁先执行,DOM 可能还没建好
async 让下载与解析完全并行,但执行是“抢跑制”:哪个脚本先下载完,就立刻中断当前解析、同步执行——不管 DOM 是否构建完毕,也不管其他脚本是否已加载。
适用条件:
- 脚本完全独立(不读取/修改 DOM,不依赖其他 JS)
- 执行时机不敏感(如统计埋点、广告 SDK、错误上报)
关键限制:
-
async对内联脚本无效,<script async>...</script>的async不起作用 - 多个
async脚本执行顺序不可预测,a.js和b.js的console.log输出顺序可能每次不同 - 若脚本里写了
document.write或直接操作document.body,大概率报错或覆盖内容
type="module" 默认 defer 但有额外约束
type="module" 脚本默认行为类似 defer:并行下载、按序执行、不阻塞解析,但它还自带现代特性支持(ESM、顶层 await、静态导入分析),同时引入了新规则。
必须注意的差异:
- 模块脚本自动启用 CORS,外链必须支持跨域(否则 404 或被拦截)
- 模块内
import是静态声明,不能动态拼接路径;想按需加载得用import()动态导入 - 模块脚本默认是严格模式,且作用域隔离——变量不会泄露到全局,
window.xxx不再自动挂载 - 如果混用传统脚本和模块脚本,
defer和type="module"的执行顺序由 HTML 位置决定,但模块内部的import依赖图优先级更高
复杂点在于:模块加载失败(如 404 或语法错误)会静默终止整个模块图,且不触发 error 事件,调试时容易漏掉——得靠 window.addEventListener('error', ...) 或服务端日志兜底。



















