首屏文本节点必须直接写在<body>后且关键CSS内联,弱网下需JS动态注入最小样式。具体包括:<main>为<body>首个子元素,内含无嵌套的<h1>和段落;仅内联LCP所需基础排版规则;fetchpriority="high"须配loading="eager";弱网探测后立即注入system-ui字体等降级样式。

首屏文本节点必须直接写在 <body> 后面
浏览器是流式解析 HTML 的,<h1>、<p>、<button> 这些文本级元素越早出现在源码里,就越早进入 DOM 构建队列。一旦被包进 <div class="container">、<section data-lazy="true"> 或任何无语义 wrapper 里,哪怕只多一层,文本就晚几百毫秒进渲染树——弱网下这几百毫秒就是白屏和可读的分界线。
常见错误现象:
- 用
<template>或<script type="text/template">存标题,JS 未执行前 DOM 为空 - CMS 输出占位符如
<div data-content="title"></div>,没 JS 就不渲染 -
display: contents替代文本父容器,Safari 15.4 以下版本直接丢掉文本上下文
正确做法:<main> 必须是 <body> 第一个子元素,里面紧跟着 <h1> 和核心段落,不嵌套、不延迟、不隐藏。
关键 CSS 必须内联且只保留排版基础规则
外部 CSS 在 3G 下 pending 超 2 秒是常态,而浏览器必须等 CSSOM 构建完成才能生成渲染树。把整站样式塞进 <style> 里反而更糟:体积大导致解析慢,实测超过 1.2KB 时 LCP 平均推迟 300ms+。
立即学习“前端免费学习笔记(深入)”;
只内联 LCP 元素真正需要的规则:
-
font-family(优先声明system-ui)、font-size、line-height、color、font-weight -
margin-top/margin-bottom等影响文本垂直节奏的基础值 - 禁用
@import、calc()、复杂选择器(如.header .nav ul li a)——它们拖慢 CSSOM 解析
非关键样式(栅格、动画、主题色)统一用 <link rel="stylesheet" media="print" onload="this.media='all'"> 延迟加载。
fetchpriority="high" 必须配对 loading="eager"
fetchpriority="high" 单独加在 <img> 或字体 <link> 上并不保险。浏览器资源调度依赖双信号:优先级 + 加载策略。默认所有 <img> 都是 fetchpriority="low",哪怕它就在 <h1> 下面,也可能被推迟到 DOMContentLoaded 之后才发请求。
必须提权的资源:
- 首屏
<h1>所在容器的背景图 → 改成<img src="..." fetchpriority="high" loading="eager"> - 关键字体文件(
.woff2)→<link rel="preload" href="font.woff2" as="font" crossorigin fetchpriority="high"> - 非可视资源(埋点脚本、广告 SDK)统一设
fetchpriority="low",不抢文本通道带宽
漏掉 loading="eager",浏览器仍可能按启发式策略懒加载图片,导致首屏文字有、但背景图迟迟不出现,容器高度塌缩,间接拖慢文本可见性。
弱网降级必须由 JS 动态注入最小可用样式
CSS 无法感知网络状态变化,也不能执行条件逻辑。等 CSS 超时再 fallback,用户早就刷新三次了。真正的兜底必须由 JS 主动触发,且不能依赖 DOMContentLoaded。
探测逻辑要实际:
- 用
fetch发两个轻量探测请求(如/health),连续两次响应时间 > 800ms 即标记为弱网 - 立刻注入内联
<style>,覆盖原有样式,仅作用于h1、p、span等文本元素 - 降级样式只含:
font-family: system-ui, sans-serif、font-size: 14px、color: #333、line-height: 1.5
这个逻辑必须写在 <head> 里尽早执行;若塞进 React/Vue 组件或等 hydration 完再跑,就晚了。最易忽略的是:文本能“显示”不等于“可用”,降级后仍需确保 lang 属性存在,避免字体 fallback 引发重排(CLS)。



















