未加 async 或 defer 的脚本会阻塞 html 解析,导致白屏和交互卡死;内联脚本立即执行并挂起 gui 线程;dom 频繁读写触发回流加剧卡顿;html 结构错误会使脚本被忽略;假死常由同步脚本、长任务与 dom 操作叠加造成。

脚本没加 async 或 defer 就一定阻塞解析
只要 <script src="xxx.js"></script> 出现在 里,且没带 async 或 defer,浏览器就会暂停 HTML 解析、等待脚本下载并执行完,再继续建 DOM 树。白屏时间直接拉长,用户点不动、输不了——这不是“慢”,是“卡死”。
- 哪怕脚本内容只有一行
console.log(1),HTTP 请求本身也会触发阻塞 -
<script>alert(1)</script>这种内联脚本更狠:不走网络,但立刻执行、立刻挂起 GUI 线程 - 移动端尤其敏感,50ms 以上的单次 JS 执行就可能丢帧,连续执行等于锁死触摸响应
DOM 操作密集导致渲染线程被挤占
JS 引擎和 GUI 渲染线程互斥。你在循环里反复读写 offsetHeight、style.left 或调用 document.getElementById(),每读一次都可能触发回流(reflow),每次回流都要等 JS 执行完才能画,结果就是“点了按钮没反应”“滚动卡成幻灯片”。
- 避免在 for 循环中查 DOM:
for (let i = 0; i → 提前缓存 <code>const list = document.querySelectorAll('[id^="item-"]'); - 批量修改样式优先用 class 切换,而不是逐个设
el.style.xxx - 禁用
document.write()—— 它会清空当前文档并重建 parser,强制重排,现代浏览器已标记为废弃
长任务未拆分引发主线程持续占用
一个函数执行超过 50ms,浏览器就判定为“长任务”,用户交互事件(点击、输入)会被压在队列尾部,直到它跑完。常见于数据处理、字符串匹配、深度遍历等场景,不是报错,但页面彻底失联。
- 用
setTimeout(fn, 0)或queueMicrotask()把大循环切成小块,每轮让出控制权 - 对数组操作优先用
Array.prototype.map/filter/reduce,它们内部优化更好;手写 for 时加if (i % 100 === 0) await new Promise(r => setTimeout(r)); - 第三方 SDK(如统计、客服浮窗)若含同步初始化逻辑,用
Web Worker包裹或延迟加载,别让它和首屏脚本抢主线程
HTML 结构错误让脚本根本没机会执行
脚本不报错、也不运行?先看结构是否合法。浏览器纠错能力有限, 没闭合、 缺开头、<script></script> 写在 外但又没 包裹——这些都会让脚本被忽略或延迟到极晚时机。
- 必须有
,否则进 Quirks 模式,<code>defer行为异常 - 检查开发者工具的 Elements 面板:如果
<script></script>标签没出现在 DOM 树里,说明 HTML 解析阶段就被跳过了 - 用
console.log内联验证基础执行环境:<script>console.log('base ok');</script>放在前,没输出=结构崩了
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











