web性能之相关路径——AI总结

这篇文章是 Google 官方 Web 开发者网站(web.dev)中关于 Web 性能优化 的核心教程,主要讲解了"关键渲染路径(Critical Rendering Path,简称 CRP)"的概念。

简单来说,它解释了:当浏览器拿到 HTML 代码后,到底经历了哪些步骤,才把一个空白的屏幕变成用户能看到的网页,以及我们如何优化这个过程让网页打开得更快。

以下是这篇文章的核心干货总结:

1. 什么是关键渲染路径 (CRP)?

关键渲染路径是指浏览器在首次在屏幕上渲染(画出)网页内容之前,必须执行的一系列步骤。 它的核心目的在于平衡:既不能太早渲染(导致网页格式错乱、空白或破损),也不能太晚渲染(让用户看着白屏等太久)。

2. 浏览器渲染网页的 8 个步骤

当浏览器下载完 HTML 后,会按顺序执行以下操作:

  1. 构建 DOM 树:把 HTML 文本解析成浏览器看得懂的网页结构(文档对象模型)。
  2. 构建 CSSOM 树:把 CSS 样式表解析成样式结构(CSS 对象模型)。
  3. 执行 JavaScript:运行会修改 DOM 或 CSSOM 的 JS 代码。
  4. 合并渲染树 (Render Tree) :把 DOM 和 CSSOM 结合起来,确定哪些节点需要显示、各自应用什么样式。
  5. 布局 (Layout) :计算每个元素在屏幕上的具体位置和大小(排版)。
  6. 绘制 (Paint) :在内存中把元素的像素(颜色、边框、背景等)画出来。
  7. 合成 (Composite) :如果层级有重叠,把不同的图层合并。
  8. 显现 (Display) :最终把像素真正绘制到用户的屏幕上。

3. 谁在拖慢网页的首屏加载?

并不是所有资源都会阻止网页的第一次渲染。文章把资源分成了两类:

🚫 会阻塞的资源(必须等它们下载并处理完,网页才能显示)

  • HTML 的基础部分
  • <head> 中的 CSS:默认会"阻塞渲染"。浏览器不看完 CSS,绝对不敢轻易画页面,怕画出来的奇丑无比。
  • <head> 中的同步 JavaScript:默认会"阻塞解析器"和"阻塞渲染"。因为 JS 随时可能删改网页内容,浏览器遇到 JS 只能停下手里所有的 HTML 解析工作,等 JS 执行完。

✅ 不会阻塞初始渲染的资源(可以边渲染边加载)

  • 图片、字体:它们加载慢,浏览器会先给它们留出空白,等加载完了再填进去(但如果不提前写好尺寸,图片加载出来后可能导致页面突然"跳动",这被称为 CLS 布局偏移)。
  • 非关键 JS/CSS :比如放在页面最底部的脚本,或者带有 deferasync 属性的 JS。

4. 延伸:内容渲染路径 (LCP)

过去大家只关注"第一次出画面(First Paint)",但现在更关注 LCP(最大内容渲染) ------即用户真正关心的核心大图或大段文字什么时候出来。优化关键路径,最终就是为了让用户更早看到有用的信息。

提问

非关键 JS/CSS:比如放在页面最底部的脚本,或者带有 defer、async 属性的 JS。这个的话,也会改变页面的元素吧?比如css,先加载完head的css,再加载body的css,会不会出现那种颜色转变的情况

你直觉非常敏锐!你的推断完全正确:确实会出现这种情况。 在前端开发中,这种因为 CSS 加载滞后导致页面样式突然跳变或闪烁的现象,有一个专门的术语叫做 FOUC(Flash of Unstyled Content,无样式内容闪烁)

下面我们具体拆解一下,你提到的非关键 JS 和 CSS 延迟加载时,页面究竟会发生什么:

1. 延后加载 CSS:确实会导致"颜色转变"或"结构跳动"

正如你所说,如果把一部分 CSS 放在 <body> 底部,或者通过 JavaScript 异步加载,浏览器处理的过程是这样的:

  1. 初始渲染 :浏览器解析完 <head> 里的关键 CSS,发现页面结构已经有了,于是启动"关键渲染路径",把网页画出来。此时,页面用的是 <head> 里的临时样式(或者干脆是浏览器的默认白底黑字)。
  2. 异步 CSS 下载完成:当页面已经显示在用户眼前时,底部的非关键 CSS 终于下载并解析完了(比如你提到的改变背景色的新样式)。
  3. 重新渲染(重绘/重排) :浏览器发现样式表更新了,被迫重新计算受影响元素的样式。

结果就是: 用户会看到网页先是 A 颜色(或没有颜色),突然闪烁一下,变成了 B 颜色。如果这个 CSS 还包含字体大小、宽高的改变,整个页面甚至会像地震一样"抖动"一下。

优化共识 :为了避免这种糟糕的视觉闪烁,行业内的标准做法是:绝对不要把会影响首屏视觉(如头部导航、首屏大图、主体背景色)的 CSS 延后加载。 只有用户滚动页面才能看到的"首屏下方"的样式,才适合做非关键 CSS 延迟加载。

2. 带有 deferasync 的 JS:也会改变元素吗?

会,而且它们的核心工作往往就是改变元素。 你可能会问: "既然 JS 会改变 DOM 元素,那浏览器为什么还敢让它们延迟(defer/async)执行,而不怕页面闪烁呢?"

这就是 deferasync 的精妙之处,它们对待"改变元素"的态度截然不同:

🛠️ defer(延迟执行)------ 稳健的网页改造者

  • 特点 :它在后台下载,但一定会等到整个 HTML 解析完成(DOM 树构建完毕)之后,并在 DOMContentLoaded 事件触发之前执行。
  • 执行时机:此时,浏览器可能还没有进行第一次正式的像素绘制(Paint)。JS 在这时候修改元素、绑定点击事件,用户是完全感知不到"变化过程"的,因为当用户看到画面的那一刻,JS 已经悄悄把网页改造完了。
  • 适用场景:绝大多数需要操作 DOM 元素的业务逻辑脚本。

async(异步执行)------ 乱序的野蛮生长者

  • 特点 :它在后台下载,什么时候下载完,就立刻暂停 HTML 解析并执行。它的执行时机是完全不可控的。
  • 执行时机:如果它下载得很慢,此时页面可能已经渲染好并呈现给用户了。一旦它突然执行并修改了页面上的元素(比如插入一个广告、改变一段文字),用户就会看到页面内容突然"无中生有"或发生跳变。
  • 适用场景 :它不适合 用来做依赖页面元素的交互。它通常用于与页面样式无关的独立脚本,比如 百度统计、谷歌分析(Analytics)代码、广告追踪脚本 等。即使它们迟到或者突然执行,也不会影响核心用户体验。

总结

你的顾虑非常高瞻远瞩。在实际的 Web 性能优化中,我们面临的正是这种两难选择

  • 如果把资源都放在 <head> 里,页面载入慢(白屏时间长),但一旦显示就是完美的。
  • 如果把资源都往后挪(异步化),页面载入极快,但很容易出现你提到的颜色转变、内容闪烁或布局跳动

优秀的开发者所做的,就是在这两者之间找到平衡:只把决定首屏长相的最少 CSS 留在 <head> 中,其余的全部延后。

那同步的css就不会吗?

同步的 CSS 也可能会,但它的"闪烁"机制和异步 CSS 完全不同。 这里有一个非常精妙的浏览器保护机制。我们先给出一个核心结论:

  • 异步 CSS 导致的闪烁 :是网页从"丑(无样式)"突然变成"美(有样式)"。
  • 同步 CSS 导致的闪烁 :通常是网页在加载过程中,由于代码写得不够好,导致元素从"位置 A"突然跳到 "位置 B" ,或者从 "没加载好"变成"加载好"。

为了让你彻底明白,我们需要拆开来看同步 CSS 在 <head> 中和在 <body> 中分别是怎么表现的:

1. 如果同步 CSS 放在 <head> 里(标准做法)

结论:绝对不会出现"颜色突然转变"的闪烁,但可能会有短暂的白屏。

这是因为浏览器有一个铁律: <head> 里的同步 CSS 会彻底阻塞网页的首次渲染。

当浏览器解析到 <head> 里的 <link rel="stylesheet"> 时,它会做两件事:

  1. 暂停把后面的 <body> 渲染到屏幕上(保持白屏)。
  2. 全速下载并解析这个 CSS 文件,构建出完整的 CSSOM 树。

只有当 <head> 里的同步 CSS 全部下载并解析完成后,浏览器才会把合并好的"渲染树"画到屏幕上。因此,用户看到网页的第一眼,它就是带着正确颜色的、完美的。它跳过了"丑陋"的阶段,代价是用户在下载 CSS 时需要多看一会儿白屏。

2. 如果同步 CSS 错放在 <body> 中间(非标准做法)

结论:一定会发生闪烁!而且会造成严重的"逐步渲染闪烁"。

虽然它是"同步"的,但由于它被放在了 HTML 内容的中间,浏览器的处理会变得非常尴尬。现代浏览器(如 Chrome 和 Firefox)为了不让用户看太久白屏,通常会采用"分段阻断""强制闪烁"的策略。

想象一下这个场景:

HTML

xml 复制代码
<body>
  <div class="header">我是头部(黑色背景)</div>
  
  <link rel="stylesheet" href="main.css"> 
  
  <div class="content">我是主体内容</div>
</body>

当浏览器从上往下解析这段 HTML 时:

  1. 渲染上半部分 :浏览器先看到了 .header,此时还没看到后面的 CSS。为了不让用户干等,浏览器会先把 .header 按照默认样式(可能没有背景色)画在屏幕上
  2. 遇到同步 CSS :浏览器突然读到了 main.css。因为它是同步的,浏览器必须停下来下载它。
  3. 样式覆盖与闪烁 :当 main.css 下载完后,浏览器发现:"噢!原来 .header 是黑色背景啊!"于是浏览器被迫重新绘制 .header

结果: 用户会眼睁睁看着 .header 先跳出来(无样式),然后随着 main.css 下载完毕,它突然闪烁一下变成了黑色。

总结:为什么我们要追求"关键 CSS 内联"?

不管是同步还是异步,只要不符合浏览器的渲染天性,都会出问题:

  • 全部同步放 <head> :页面不闪烁,但如果 CSS 文件太大,用户会面对长时间的白屏,体验极差。
  • 异步加载 / 放底部 :页面秒开,但会发生你提到的样式突然转变和闪烁(FOUC) ,体验同样很差。

为了解决这个两难问题,目前前端性能优化的最高境界叫做 关键 CSS(Critical CSS)

找出决定网页首屏(用户一眼看到的区域)长相的最少 CSS 代码 (通常只有几 KB),直接内联(Inline)写在 <head><style> 标签里。 而剩下的大部队 CSS(比如滚动下去才能看到的底部样式、弹窗样式),则使用异步加载

这样既保证了首屏秒开(解决白屏),又保证了首屏出来时样式就是完美的(解决闪烁),两全其美!

相关推荐
掘金小豆1 小时前
Spring 事务失效的 6 大场景,你踩过几个?
后端·spring·面试
不好听6131 小时前
从零搭建一个 RAG 语义搜索系统 —— DEMO的初始阶段
javascript·面试·llm
何时梦醒1 小时前
上下文工程(Context Engineering):AI 应用开发的新范式 —— 从理论到实战全解析
javascript
竹林8181 小时前
用 wagmi v2 踩坑两天,我终于搞懂了多链钱包切换在 DeFi 前端中的正确姿势
前端·javascript
用户2136610035721 小时前
Vue项目搜索功能与面包屑导航
前端·javascript
星栈1 小时前
LiveView 的实时通信,爽是爽,但 PubSub 和广播也最容易把自己绕晕
前端·前端框架·elixir
用户2930750976691 小时前
告别关键词匹配,拥抱向量语义 —— RAG 搜索从零到一
前端
黄敬峰1 小时前
RAG 实战:从关键词匹配到语义搜索,手把手教你用 Node.js 搭建 AI 检索引擎
面试
独孤留白1 小时前
从C到Rust:告别 C 的"指针 + 长度"手动模式
前端·rust