HTML结构是浏览器渲染的第一道闸门,DOM节点出现时机、嵌套深度(超6层触发重排)及script阻塞(未加defer/async会致首屏白屏)直接决定首屏性能。

HTML结构不是“写完能跑就行”的中间产物,而是浏览器渲染流水线的第一道闸门——结构一卡,后面所有优化都白搭。关键不在于标签多不多,而在于DOM节点何时出现、嵌套多深、是否阻塞解析。
为什么<script>放在<head>里会让首屏白屏两秒</script>
浏览器解析HTML是流式的,遇到未加修饰的<script>就暂停解析、同步下载并执行,等它完事才继续构建DOM树。哪怕只是<script>console.log(1)</script>写在<head>里,首屏图片、标题、导航全得干等。
- 非关键脚本(统计、埋点、第三方SDK)一律加
defer:按顺序下载+执行,不阻塞HTML解析 - 纯异步逻辑(如广告加载)可用
async,但执行时机不可控,document.getElementById可能返回null - 真要操作DOM的脚本,放
</body>前——比DOMContentLoaded事件更稳,实测晚100ms加载,LCP就晚100ms - 内联脚本(
<script>...</script>)永远阻塞,除非只做极轻量初始化(如设置window.__INIT__)
DOM嵌套超6层会触发强制重排
浏览器每创建一个DOM节点,都要做样式匹配、布局计算。嵌套越深,CSS选择器路径越长,getBoundingClientRect()这类读取操作越容易触发强制同步布局(forced reflow),尤其在低端安卓机上,FCP延迟明显。
- 用Chrome DevTools → Elements面板右键任意节点 → “Show DOM properties”,查
depth值;超过6层就要警惕 - 用
<main>、<section>、<article>替代无意义的<div class="wrapper"><div class="inner"><div class="content"> - 避免在
<table>里嵌套<div>:表格单元格需整行解析完才能开始渲染,重排成本更高 - Flexbox/Grid能实现的布局,别靠三层
<div>堆出来
首屏内容位置靠后直接拉垮LCP指标
浏览器不会“跳着解析”。<h1>、<img fetchpriority="high">、<form>如果写在HTML底部,哪怕体积再小,也得等前面所有标签解析完才进DOM树——LCP自然被拖长。
立即学习“前端免费学习笔记(深入)”;
- 把首屏核心元素尽量前置:
<main>、<h1>、首图、主按钮,别为了模板复用牺牲结构优先级 - 导航栏、广告位、埋点脚本这类非首屏内容,考虑用
loading="lazy"+fetchpriority="low"降权,或拆成<template>+ JS动态插入 - 绝对不要在
<head>里用<script>动态生成首屏DOM——这等于把原生流式解析退化成JS重建,完全绕开浏览器优化 -
<meta charset="utf-8">必须出现在前1024字节内,否则浏览器可能先按latin1解析一部分再重载,导致闪动或乱码
图片没设width/height会引发布局抖动
loading="lazy"只控制资源何时下载,不解决尺寸预留问题。图片缺失width/height,浏览器无法预留空间,加载完成瞬间触发重排,文字上跳、按钮位移肉眼可见。
- 所有
<img>必须显式声明width和height(可用CSSaspect-ratio替代,但注意兼容性) -
decoding="async"应与loading="lazy"同时使用,避免解码阻塞主线程 - SVG图标建议内联(
<svg></svg>),既免请求又可控尺寸,比<img src="icon.svg">更稳 - 服务端启用分块传输(
Transfer-Encoding: chunked),让首屏区块(header + 导航)几毫秒内就能推送到浏览器
真正难的不是知道该怎么做,而是每次写<div>前,得下意识问一句:这个节点是不是此刻就必须存在?它会不会让浏览器多走一步?



















