dom解析遇到无async/defer的script会同步阻塞:浏览器暂停html解析,下载并执行脚本后才继续,导致白屏;defer等dom构建完按序执行,async下载完立即执行且无序。

DOM 解析遇到 <script></script> 时为什么会卡住
浏览器解析 HTML 是单线程线性过程,遇到 <script src="app.js"></script> 会立刻暂停 DOM 构建,转而下载、解析、执行脚本——哪怕这个脚本只用来上报 PV。此时用户看到白屏,不是网络慢,是浏览器在等它。
常见错误现象:
-
DOMContentLoaded时间比 HTML 加载完成晚 1.2s,但 Network 面板里app.js只有 80KB —— 很可能是没加defer的同步加载拖累了整个 DOM 构建 - 首屏文字渲染延迟,但控制台没报错 —— 实际是
<script></script>放在里且无属性,阻塞了后续 HTML 解析
关键判断:只要 <script></script> 标签没带 async 或 defer,就默认同步阻塞。这不是“可能”,而是浏览器规范行为。
defer 和 async 的执行时机与依赖关系怎么选
二者都让脚本下载不阻塞 HTML 解析,但执行逻辑完全不同,选错会导致 DOM 拿不到元素、初始化失败或顺序错乱。
实操建议:
- 业务主逻辑(如 Vue.createApp、轮播图 init)→ 必须用
defer:<script defer src="main.js"></script>,它保证 DOM 解析完才执行,且按书写顺序,适合依赖 DOM 节点的代码 - 统计/广告类脚本(如
analytics.js)→ 用async:<script async src="tracker.js"></script>,它下载完立刻执行,不保证顺序,也不等 DOM 就绪,需自行判断document.readyState - 绝对不要混用:同一组脚本中,有的加
defer、有的不加,会导致执行时机不可控;type="module"默认等价于defer,但支持 ES 模块语法,适合现代项目
容易踩的坑:把 async 用在需要操作 document.getElementById('app') 的脚本上,结果执行时 DOM 还没生成,拿不到节点。
内联 <style></style> 多大才算“关键”,超过多少会适得其反
内联 CSS 不触发网络请求,能让 DOM 和 CSSOM 并行构建,缩短关键渲染路径。但体积不是越大越好。
实操建议:
- 内联内容必须严格限定为「首屏可见区域」用到的样式,例如登录页只需
.logo、.login-form、.submit-btn的基础尺寸、颜色、字体大小 - 体积控制在
~1KB内(压缩后),超限会显著拖慢 TTFB 和初始 HTML 解析,尤其在 3G 网络下更明显 - 别手写提取——用
critters(Vite 插件)或purgecss + html-webpack-plugin自动分析 HTML 中实际用到的选择器,否则容易漏或冗余 - 非关键 CSS 用
<link rel="stylesheet" href="non-critical.css" media="print" onload="this.media='all'">延迟加载,避免阻塞渲染树构建
性能影响:内联 5KB CSS 看似“一步到位”,实际会让 HTML 主文档体积翻倍,弱网下首字节到达时间(TTFB)增加 300ms+,得不偿失。
首屏图片加 loading="lazy" 反而拉长 LCP 怎么办
loading="lazy" 是懒加载开关,但对首屏关键图启用它,等于告诉浏览器“先别加载”,直接导致最大内容绘制(LCP)延迟。
实操建议:
- 首屏图片(
<img>出现在视口内或紧邻上方)→ 不加loading属性(默认eager),确保立即加载 - 非首屏图片 → 统一加
loading="lazy",但必须同时声明width和height属性,否则加载时尺寸未知,触发布局重排(CLS) - SSR 页面中由 JS 动态插入的
<img>→ 手动设置img.loading = 'lazy',因为 JS 创建的元素默认是eager,和预期不符
容易被忽略的地方:很多团队只关注“加了 lazy 就省流量”,却没检查它是否出现在 LCP 元素上——Chrome DevTools 的 Performance 面板里直接标出哪个元素是 LCP,优先确认它是否被 lazy 误伤。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











