探索网页绘制:优化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,减少浏览器端的下载和解析时间。
相关推荐
阿伟来咯~24 分钟前
记录学习react的一些内容
javascript·学习·react.js
吕彬-前端29 分钟前
使用vite+react+ts+Ant Design开发后台管理项目(五)
前端·javascript·react.js
学前端的小朱32 分钟前
Redux的简介及其在React中的应用
前端·javascript·react.js·redux·store
guai_guai_guai41 分钟前
uniapp
前端·javascript·vue.js·uni-app
也无晴也无风雨42 分钟前
在JS中, 0 == [0] 吗
开发语言·javascript
bysking2 小时前
【前端-组件】定义行分组的表格表单实现-bysking
前端·react.js
王哲晓2 小时前
第三十章 章节练习商品列表组件封装
前端·javascript·vue.js
fg_4112 小时前
无网络安装ionic和运行
前端·npm
理想不理想v2 小时前
‌Vue 3相比Vue 2的主要改进‌?
前端·javascript·vue.js·面试
酷酷的阿云2 小时前
不用ECharts!从0到1徒手撸一个Vue3柱状图
前端·javascript·vue.js