探索网页绘制:优化CSS渲染流水线,缩短白屏时间

渲染流水线下的CSS

在Web开发中,渲染流水(Rendering Pipeline)是浏览器将HTML、CSS和JavaScript转换为用户可见页面的过程。 在这个过程中,CSS在渲染流水线中扮演着重要的角色。

浏览器加载和渲染CSS样式的过程通常分为以下几个步骤:

  1. 发起网络请求: 浏览器在解析HTML文档的过程中,遇到 \<link> 标签和 \<style> 标签(或者外部CSS文件)时,会发起网络请求,请求对应的CSS文件。
  2. 下载CSS文件: 浏览器根据发起的CSS请求,向服务器下载CSS文件。下载过程中,浏览器可能会进行一些优化,如使用缓存,检查文件是否已被修改,以决定是否需要重新下载。
  3. 构建CSSOM树 一旦CSS文件被下载,浏览器开始解析CSS,并构建CSS对象模型(CSSOM)树。这是一个表示CSS样式层次结构的树形结构,包含了CSS规则和它们的层次关系。
  4. 构建Render树 Render树是由DOM树CSSOM树合并而成的,包含了需要渲染的所有节点。在这个阶段,浏览器确定页面上哪些元素需要被渲染,以及它们的样式信息。
  5. 布局(Layout): 布局阶段确定了每个渲染树节点的确切位置和大小。这个过程称为布局或合成,浏览器计算每个元素在屏幕上的准确位置。
  6. 绘制(Paint): 在布局后,浏览器进入绘制阶段,将页面上的元素绘制成像素。这个阶段包括绘制元素的内容、边框和背景等。
  7. 合成(Composite): 最后,浏览器通过GPU加速将各个图层合成为最终的页面。这提高了渲染性能,特别是在处理复杂的动画和变换时。

为什么浏览器需要cssom树

CSSOM(CSS Object Model)树是浏览器在渲染网页时构建的一个树形结构,它表示了CSS样式的层次结构和规则。 浏览器需要CSSOM树的主要原因包括:

  • 样式计算(Style Calculation): 在构建渲染树之前,浏览器需要计算每个元素的最终样式。这涉及到解析CSS规则并应用它们,考虑到继承、层叠等因素。CSSOM树提供了表示这些样式信息的数据结构,以便在后续的渲染流程中使用。
  • 渲染树的构建: 渲染树是由DOM树CSSOM树结合而成的,用于确定在页面上需要渲染的元素。这意味着只有在构建CSSOM树后,浏览器才能准确地知道哪些样式规则应用于哪些元素,从而构建出最终的渲染树。
  • 样式修改和计算:JavaScript或用户交互导致样式发生变化时,浏览器需要更新渲染树。CSSOM树提供了一种有效的方式来管理和计算这些样式变化,以便及时更新渲染。
  • 性能优化: 通过将样式信息组织成CSSOM树,浏览器可以更高效地进行样式计算和渲染操作。这有助于提高页面渲染性能,尤其是在复杂的Web应用中。
  • 开发者工具: CSSOM树也为浏览器的开发者工具提供了基础数据,使开发人员能够检查和调试应用的样式信息。

影响页面展示的因素以及优化策略

渲染流水线会影响首次页面展示的速度,而首次页面展示的速度又直接影响到用户的体验。

从发起URL请求到首次页面在视觉上显示,整个过程通常可以划分为三个主要阶段:请求阶段、渲染阶段和交互阶段。

1. 请求阶段(Request Phase):

  1. DNS解析:浏览器解析URL中的域名,将其转换为对应的IP地址。这是一个将域名映射到实际服务器地址的过程。
  2. 建立TCP连接:浏览器通过域名解析获得服务器IP地址后,会尝试建立TCP连接。这涉及到一系列的握手过程,包括三次握手。
  3. 发送HTTP请求:浏览器通过已建立的TCP连接向服务器发送HTTP请求。该请求包括获取HTML文档的请求以及可能的其他资源请求(例如CSS、JavaScript、图片等)。
  4. 服务器处理请求:服务器接收到请求后,开始处理并返回相应的资源。处理过程可能包括从数据库中检索数据、执行服务器端的逻辑等。
  5. 接收响应:浏览器接收到服务器的响应,其中包含HTML文档以及其他请求的资源。这些资源被逐一请求和接收。

2. 渲染阶段(Rendering Phase):

  1. HTML解析和构建DOM树:浏览器开始解析HTML文档,构建DOM(Document Object Model)树,表示页面的结构。
  2. CSS解析和构建CSSOM树:浏览器解析CSS文件,构建CSSOM(CSS Object Model)树,表示页面的样式信息。
  3. 构建Render树:浏览器将DOM树和CSSOM树结合,构建Render树。Render树包含需要渲染的元素和样式信息,用于确定元素在页面上的位置和大小。
  4. 布局(Layout):浏览器进行布局,计算每个元素在屏幕上的确切位置和大小。这个过程决定了每个元素在渲染时的几何形状。
  5. 绘制(Paint):浏览器根据Render树和布局信息,进行绘制操作,将页面上的元素绘制成像素。这是将样式应用到元素并生成位图的阶段。
  6. 合成(Composite):最后,浏览器将绘制好的图层进行合成,形成最终的页面。这个过程通常使用GPU进行加速,以提高性能。

3. 交互阶段(Interaction Phase):

  1. JavaScript执行:如果在HTML中存在JavaScript脚本,浏览器会执行这些脚本。脚本的执行可能导致DOM的修改、样式的变化以及其他交互行为。
  2. 事件处理:浏览器处理用户交互事件,例如点击、滚动等。这可能触发进一步的JavaScript执行或样式变化。
  3. 动画和效果:如果有CSS动画或过渡效果,它们将开始执行,增加页面的动态性。

影响第一个阶段的主要因素在网络以及服务器处理方面。

第二阶段的主要问题是白屏时间,这个阶段主要又解析HTML、下载css、下载JavaScript、生成cssom、执行JavaScript、生成布局树、绘制页面。在这一阶段主要的瓶颈是下载css文件、下载JavaScript文件以及执行JavaScript。

白屏时间优化策略:

1. 优化CSS文件的下载和解析:

a. 使用媒体查询和断点布局:

  • 针对不同设备和屏幕大小,使用媒体查询来加载不同的CSS文件,以减少不必要的下载和解析。
html 复制代码
<link rel="stylesheet" media="screen and (max-width: 600px)" href="mobile.css">

b. 内联关键CSS(Critical CSS):

  • 将关键的CSS内联到HTML文件中,确保首次渲染时只加载必需的样式,延迟加载其余的CSS。
html 复制代码
<style>
  /* 关键CSS代码 */
</style>
<link rel="stylesheet" href="remaining.css">

2. 优化JavaScript文件的下载和执行:

a. 异步加载非关键JavaScript:

  • 将非关键的JavaScript文件进行异步加载,不影响首次页面的渲染。
html 复制代码
<script async src="non-critical.js"></script>

b. 推迟JavaScript执行:

  • 使用defer属性推迟JavaScript的执行,确保在DOM解析完成后执行脚本。
html 复制代码
<script defer src="your-script.js"></script>

3. 延迟执行JavaScript:

a. 按需加载:

  • 仅在需要时加载JavaScript文件,而不是在首次页面加载时就下载全部脚本。这可以通过动态创建<script>标签或使用现代的模块化加载工具实现。
js 复制代码
// 动态创建script标签
var script = document.createElement('script');
script.src = 'your-script.js';
document.body.appendChild(script);

b. 分割代码(Code Splitting):

  • 将大型JavaScript文件拆分为小块,仅在需要时动态加载。
js 复制代码
// 通过动态import实现代码分割
import('your-module.js').then(module => {
  // 模块加载完成后执行
});

4. 优化CSSOM的生成和页面渲染:

a. 使用媒体查询:

  • 针对不同媒体查询条件,使用媒体查询来延迟加载CSS文件,减轻渲染阶段的压力。
css 复制代码
/* 媒体查询中的样式规则 */
@media screen and (min-width: 600px) {
  /* 样式规则 */
}

b. 最小化关键CSS的规模:

  • 确保关键CSS的规模尽可能小,以加快CSSOM的生成速度。

5. 使用服务端渲染(SSR):

  • 对于某些场景,考虑使用服务端渲染,以在服务器端生成HTML、CSS和JavaScript,减少浏览器端的下载和解析时间。
相关推荐
爱分享的程序员1 分钟前
前端面试专栏-算法篇:17. 排序算法
前端·javascript·node.js
lichenyang4532 分钟前
react案例动态表单(受控组件)
前端
Jackson_Mseven2 分钟前
面试官:useEffect 为什么总背刺?我:闭包、ref 和依赖数组的三角恋
前端·react.js·面试
哎呦你好19 分钟前
【CSS】Grid 布局基础知识及实例展示
开发语言·前端·css·css3
盛夏绽放28 分钟前
接口验证机制在Token认证中的关键作用与优化实践
前端·node.js·有问必答
zhangxingchao1 小时前
Jetpack Compose 之 Modifier(中)
前端
JarvanMo1 小时前
理解 Flutter 中 GoRouter 的context.push与context.go
前端
pe7er1 小时前
使用 Vue 官方脚手架创建项目时遇到 Node 18 报错问题的排查与解决
前端·javascript·vue.js
星始流年1 小时前
前端视角下认识AI Agent
前端·agent·ai编程
pe7er1 小时前
使用 types / typings 实现全局 TypeScript 类型定义,无需 import/export
前端·javascript·vue.js