当前位置: 首页 > news >正文

浏览器渲染原理 - 输入url 回车后发生了什么

目录

  • 渲染时间点
  • 渲染流水线
    • 1,解析(parse)HTML
      • 1.1,DOM树
      • 1.2,CSSOM树
      • 1.3,解析时遇到 css 是怎么做的
      • 1.4,解析时遇到 js 是怎么做的
    • 2,样式计算 Recalculate style
    • 3,布局 layout
    • 4,分层 layer
    • 5,绘制 paint
    • 6,分块 tiling
    • 7,光栅化 raster
    • 8,画 draw
  • 常见面试题
    • 什么是 reflow
    • 什么是 repaint
    • 为什么 transform 效率高

在上一篇文章中,介绍了 浏览器的事件循环,其中提到了浏览器的进程模型。那浏览器是如何渲染页面的呢?

渲染时间点

浏览器会通过网络进程中的线程来通信,获取到 html 数据后生成渲染任务,发送给消息队列。

渲染主线程会执行渲染任务。整个渲染流程:把 html 字符串解析为像素点信息,再交给 GPU来渲染后在页面中展示。

在这里插入图片描述

渲染流水线

在这里插入图片描述

每个阶段都有明确的输入输出,上个阶段的输出会成为下个阶段的输入。形成一套完整的流水线。

1,解析(parse)HTML

会将 html 字符串解析为 2个树。因为字符串不好操作,对象更容易处理。

1.1,DOM树

也就是 document 对象。可以在控制台通过console.dir(document) 展示对象结构。
在这里插入图片描述

1.2,CSSOM树

包括:

  • <style> 内部样式
  • <link> 外部样式
  • style="" 内联样式
  • 浏览器默认样式表

在这里插入图片描述

注意,这里的 CSSOM树 ≠ document.styleSheets。因为 document.styleSheets 只包括内部样式外部样式,每写一个 <style><link> 就会多一个 CSSStyleSheet 样式表:

在这里插入图片描述

举例说明:

<html><head><style>body h1 {color: red;font-size: 3em;}div p {margin: 1em;color: blue;}</style></head><body><h1>下雪天的夏风</h1><div><p>求关注</p></div></body>
</html>

在这里插入图片描述

可以看到:

  • CSSStyleSheet 样式表
    • CSSStyleRule 规则对象
      • selectorText 选择器
      • style 样式(键值对)

另外,CSSStyleSheet 样式表是可以直接通过 js 操作的。

举例:通过 js 给页面所有 div 添加 border

document.styleSheets[0].addRule('div', 'border: 1px solid !important')

这样添加样式的方式,一般框架用的多。最终样式不会在内联样式中展现。

1.3,解析时遇到 css 是怎么做的

渲染主线程遇到 css 时,会启动一个预解析线程,让它来率先下载和解析 css。渲染主线程继续解析 html。

预解析线程会快速浏览,如果遇到外部样式link ,会通知网络线程来下载 css,下载完成后进行“解析”完成后交给渲染主线程。

并不是真正的解析,只是做一些前期工作,最终生成 CSSOM 树还是由渲染主线程来完成。

所以,css 代码不会阻塞解析 HTML。

在这里插入图片描述

1.4,解析时遇到 js 是怎么做的

没有生成所谓的 js 树,是因为 js 只需要执行一遍即可。DOM树和CSSOM树作为解析 HTML 的输出,后续还会有其他的操作。

渲染主线程遇到内部 js 时,直接启动 V8 引擎执行即可;遇到外部 js 时,会启动一个预解析线程,让它来下载 js,渲染主线程暂停

预解析线程会通知网络线程来下载 js,下载完成后再交给渲染主线程来执行。执行完继续解析 HTML。

这样做的原因:DOM 树是边解析边生成的,而 js 代码可能会修改之前已解析好的内容。

所以,js 代码会阻塞解析 HTML。

在这里插入图片描述

2,样式计算 Recalculate style

遍历DOM树,计算每个节点的最终样式 Computed Style。

这一过程,许多预设值会变成绝对值,比如 red 变为 rgb(255,0,0);相对单位变为绝对单位,比如rem 变为 px

最终会得到1个带有最终样式的 DOM 树。
在这里插入图片描述

可以在浏览器的 computed 窗口中,或使用 getComputedStyle() 查看某个元素的最终样式。

3,布局 layout

遍历DOM树的每个节点,根据 css 属性值计算每个节点的几何信息(尺寸,相对包含块的位置),生成一个 layout 树。

注意到 DOM 树和 layout 树不一定一一对应。
在这里插入图片描述
举例1:diaplay:none 的元素不会出现在 layout 树中。

问题来了,为什么<head> <link> 等元素默认是隐藏的?因为在浏览器默认样式表中,它们 diaplay:none
在这里插入图片描述
举例2:伪元素的 content 内容在 DOM 树中没有,在 layout 树中有。

在这里插入图片描述
举例3:内容必须在行盒中,行盒和块盒不能相邻。所以在 layout 树中会有匿名块盒

解释:“行级元素”,“块级元素”这些元素指的是 html。但元素的类型是 css 属性决定的。所以称为行盒或块盒。

在这里插入图片描述

4,分层 layer

现在 layout 布局树中每个节点的几何信息,尺寸位置等都明确了。渲染主线程会使用一套策略对整个布局树分层。

目的是提升效率,这样可以让之后页面的修改更新不会影响到其他层。类似 PS 中的图层,修改某一个图层不会影响到其他图层。

可以在浏览器控制台的 Layers 面板查看当前网页的分层信息。
也不会分太多的层,因为会比较占内存。滚动条是单独一层。

在这里插入图片描述

另外,和堆叠上下文有关的 css 属性(transform,opacity)会影响分层的决策。其中 will-change 属性能更大程度的影响分层角色,可能会将设置该属性的元素单独分一层。

因为这个属性会告诉浏览器,我可能会经常变化,浏览器最好掂量下。

5,绘制 paint

分层后,会对每层都生成绘制指令,类似于 canvas 中的 API 一样。其实canvas 用的就是浏览器内核的绘制功能。

指令举例:“笔”移动到 xx 坐标位置,画 100*100 的矩形,并用红色填充等等。

在这里插入图片描述

以上。渲染主线程的工作结束,剩下的步骤交给其他线程来完成。

在这里插入图片描述

6,分块 tiling

将每层都分为多个小的区域,浏览器视窗区域的优先级最高,靠近视窗区域的优先级次之。
在这里插入图片描述

分块逻辑:渲染主线程每个图层的绘制信息交给合成线程,合成线程又会启动多个分块线程来对每个图层进行分块。

合成线程也属于渲染线程

在这里插入图片描述

7,光栅化 raster

将每个块变成位图,位图就是每个像素点的信息。优先处理靠近视窗的块。

位图就是内存中的二位数组,其中记录了每个像素点的信息。

在这里插入图片描述

此过程会用到GPU来加速,用到显卡。
在这里插入图片描述

8,画 draw

合成线程现在拿到了所有的信息,在画之前还需要确认【指引信息 quad】,也就是位图相对的屏幕在哪里。

注意,之前布局树中的信息是相对于整个页面的。现在需要知道每个块相对于屏幕的位置信息。

步骤:合成线程将指引信息 --> GPU 进程 --> 硬件显卡,由显卡来呈现最终的像素信息。

GPU 做中转的原因是:GPU 是浏览器的进程。合成线程属于渲染进程,它是在沙盒中的,与硬件系统做隔离,提升安全性。所以渲染进程是没有调度系统硬件能力的。

在这里插入图片描述
而 css 中的 transform 属性就是在这一步完成的。这就是 transform 效率高的原因,直接跳过之前所有的步骤。

常见面试题

什么是 reflow

【recalculate layoutBlockFlow 重排】它的本质是重新计算 layout 树

当做了会影响 layout 树的操作后,比如修改几何尺寸相关的信息时,会引起重新计算 layout 树。

在这里插入图片描述

并且,为了避免连续多次的操作导致 layout 树反复计算,浏览器会合并这些操作,当 js 代码完成后统一计算。所以这一步骤是异步的。

同样因为这个原因,当 js 获取布局属性时,可能无法获取到最新的布局信息。

浏览器会在反复权衡下,最终决定获取属性时立即 reflow。

什么是 repaint

它的本质是重新根据分层信息计算了绘制指令

当改变了可见样式,比如颜色等和几何尺寸无关的属性时,就需要重新计算,会从【绘制】这一步骤开始重新执行。

而因为几何尺寸也属于可见样式,所以 reflow 一定会引起 repaint。

在这里插入图片描述

为什么 transform 效率高

因为 transform 既不会影响布局,也不会影响绘制,它只会影响渲染的最后一步【画】。而【画】是在合成线程中,不会影响到渲染主线程。同样无论渲染主线程多忙,也不会影响到 transform。

验证代码如下,当渲染主线程卡死时,transform 不受影响。

<!DOCTYPE html>
<html lang="en"><head><style>.common {width: 50px;height: 50px;background-color: salmon;border-radius: 50%;margin: 10px;}.ball1 {animation: move1 1s alternate infinite;}.ball2 {position: relative;left: 0;animation: move2 1s alternate infinite;}@keyframes move1 {to {transform: translate(100px);}}@keyframes move2 {to {left: 100px;}}</style></head><body><button id="btn">死循环3s</button><div class="common ball1"></div><div class="common ball2"></div><script>btn.addEventListener("click", function () {delay(3000);});function delay(duration) {var start = Date.now();while (Date.now() - start < duration) {}}</script></body>
</html>

效果:
在这里插入图片描述

滚动也是一样的逻辑,如果 js 有一段上面的死循环,并不会影响到滚动。因为只有视窗内元素的位置变了,直接执行【画 draw】这一步骤。

以上。


参考:渡一教育。

相关文章:

浏览器渲染原理 - 输入url 回车后发生了什么

目录 渲染时间点渲染流水线1&#xff0c;解析&#xff08;parse&#xff09;HTML1.1&#xff0c;DOM树1.2&#xff0c;CSSOM树1.3&#xff0c;解析时遇到 css 是怎么做的1.4&#xff0c;解析时遇到 js 是怎么做的 2&#xff0c;样式计算 Recalculate style3&#xff0c;布局 la…...

大文本的全文检索方案附件索引

一、简介 Elasticsearch附件索引是需要插件支持的功能&#xff0c;它允许将文件内容附加到Elasticsearch文档中&#xff0c;并对这些附件内容进行全文检索。本文将带你了解索引附件的原理和使用方法&#xff0c;并通过一个实际示例来说明如何在Elasticsearch中索引和检索文件附…...

35_windows环境debug Nginx 源码-CLion配置CMake和启动

文章目录 生成 CMakeLists.txt 组态档35_windows环境debug Nginx 源码-CLion配置CMake和启动生成 CMakeLists.txt 组态档 修改auto目录configure文件,在 . auto/make 上边增加 . auto/cmake, 大概在 106 行。在 auto 目录下创建cmake 文件其内容如下: #!/usr/bin/env bash NG…...

收集的一些比较好的git网址

1、民间故事 https://github.com/folkstory/lingqiu/blob/master/%E4%BC%A0%E8%AF%B4%E9%83%A8%E5%88%86/%E4%BA%BA%E7%89%A9%E4%BC%A0%E8%AF%B4/%E2%80%9C%E6%B5%B7%E5%BA%95%E6%8D%9E%E6%9C%88%E2%80%9D%E7%9A%84%E6%AD%A6%E4%B8%BE.md 2、童话故事 https://gutenberg.org/c…...

容斥原理 博弈论(多种Nim游戏解法)

目录 容斥原理容斥原理的简介能被整除的数&#xff08;典型例题&#xff09;实现思路代码实现扩展&#xff1a;用DPS实现 博弈论博弈论中的相关性质博弈论的相关结论先手必败必胜的证明Nim游戏&#xff08;典型例题&#xff09;代码实现 台阶-Nim游戏&#xff08;典型例题&…...

【C++】函数指针

2023年8月18日&#xff0c;周五上午 今天在B站看Qt教学视频的时候遇到了 目录 语法和typedef或using结合我的总结 语法 返回类型 (*指针变量名)(参数列表)以下是一些示例来说明如何声明不同类型的函数指针&#xff1a; 声明一个不接受任何参数且返回void的函数指针&#xf…...

VBA技术资料MF45:VBA_在Excel中自定义行高

【分享成果&#xff0c;随喜正能量】可以不光芒万丈&#xff0c;但不要停止发光。有的人陷入困境&#xff0c;不是被人所困&#xff0c;而是自己束缚自己&#xff0c;这时"解铃还须系铃人"&#xff0c;如果自己无法放下&#xff0c;如何能脱困&#xff1f; 。 我给V…...

【Git】Git中的钩子

Git Book——Git的自定义钩子 Git中的钩子分为两大类&#xff1a; 1、客户端钩子&#xff1a;由诸如提交和合并这样的操作所调用 2、服务端钩子&#xff1a;由诸如接收被推送的提交这样的联网操作 客户端钩子&#xff1a; 提交工作流钩子 pre-commit&#xff1a;在提交信息前…...

java 工程管理系统源码+项目说明+功能描述+前后端分离 + 二次开发 em

Java版工程项目管理系统 Spring CloudSpring BootMybatisVueElementUI前后端分离 功能清单如下&#xff1a; 首页 工作台&#xff1a;待办工作、消息通知、预警信息&#xff0c;点击可进入相应的列表 项目进度图表&#xff1a;选择&#xff08;总体或单个&#xff09;项目显…...

Java # JVM

一、1.8之前 运行时数据区&#xff08;进程共享&#xff09; 运行时常量池为什么要有方法区&#xff1a; jvm完成类装载后&#xff0c;需要将class文件中的常量池转入内存&#xff0c;保存在方法区中为什么是常量&#xff1a; 常量对象操作较多&#xff0c;为了避免频繁创建和…...

vscode远程连接Linux失败,提示过程试图写入的管道不存在(三种解决办法)

vscode报错如下&#xff1a; 一、第一种情况 原因是本地的known_hosts文件记录服务器信息与现服务器的信息冲突了&#xff0c;导致连接失败。 解决方案就是把本地的known_hosts的原服务器信息全部删掉&#xff0c;然后重新连接。 二、第二种情况 在编写配置文件config时&…...

elaticsearch(1)

1.简介 Elasticsearch是一个开源的高扩展的分布式全文检索引擎&#xff0c;它可以近乎实时的存储、检索数据&#xff1b;本身扩展性很好&#xff0c;可以扩展到上百台服务器&#xff0c;处理PB级别的数据。 Elasticsearch也使用Java开发并使用Lucene作为其核心来实现所有索引…...

使用pnpm workspace管理Monorepo架构

在开发项目的过程中&#xff0c;我们需要在一个仓库中管理多个项目&#xff0c;每个项目有独立的依赖、脚手架&#xff0c;这种形式的项目结构我们称之为Monorepo&#xff0c;pnpm workspace就是管理这类项目的方案之一。 一、pnpm简介 1、pnpm概述 pnpm代表performance npm…...

Ubuntu16.04-ros-kinetic环境搭建笔记=1=

tips&#xff1a;搬运资料&#xff0c;留个记录 安装Ubuntu Ubuntu官网下载地址 安装 虚拟机安装Ubuntu 最好断网安装Ubuntu&#xff0c;可以节约时间 Ubuntu基础设置 Ubuntu换国内源 换成清华源 sudo apt upgradeVMwareTool安装 把这个压缩包拖到桌面&#xff0c;否则只读…...

应用层自定义协议(组织数据的格式)

概念 在进行网络传输数据的时候&#xff0c;通常是将要传输的数据组织成一个字符串&#xff0c;再将字符串转换为一个字节流进行网络传输数据&#xff0c;而数据组织的格式是多种多样的&#xff0c;我们只需要保证&#xff0c;客户端和服务器对于字符串的组织和解析统一即可 现…...

5种常见的3D游戏艺术风格及工具栈

在游戏开发领域&#xff0c;3D 艺术风格已成为为玩家创造身临其境、引人入胜的体验的重要组成部分。 随着技术的进步&#xff0c;创造令人惊叹的 3D 视觉效果的可能性已经大大扩展&#xff0c;为游戏开发人员提供了广泛的选择。 在本文中&#xff0c;我们将探讨当今游戏开发中…...

【玩转Linux操作】crond的基本操作

&#x1f38a;专栏【玩转Linux操作】 &#x1f354;喜欢的诗句&#xff1a;更喜岷山千里雪 三军过后尽开颜。 &#x1f386;音乐分享【Counting Stars 】 欢迎并且感谢大家指出小吉的问题&#x1f970; 文章目录 &#x1f354;概述&#x1f354;命令⭐常用选项 &#x1f354;练…...

设置Linux 静态IP

LInux虚拟机默认的IP地址是动态获取的 作为服务器&#xff0c;我们一般还需要把IP地址设置为静态的 设置静态IP vim /etc/sysconfig/network-scripts/ifcfg-ens33 TYPEEthernet PROXY_METHODnone BROWSER_ONLYno # BOOTPROTOdhcp 动态获取 BOOTPROTOstatic IPADDR"192.16…...

JMeter接口自动化测试实例—JMeter引用javaScript

Jmeter提供了JSR223 PreProcessor前置处理器&#xff0c;通过该工具融合了Java 8 Nashorn 脚本引擎&#xff0c;可以执行js脚本以便对脚本进行前置处理。其中比较典型的应用就是通过执行js脚本对前端数据进行rsa加密&#xff0c;如登录密码加密。但在这里我就简单的应用javaScr…...

javascript期末作业【三维房屋设计】 【源码+文档下载】

1、引入three.js库 官网下载three.js 库 放置目录并引用 引入js文件: 设置场景&#xff08;scene&#xff09; &#xff08;1&#xff09;创建场景对象 &#xff08;2&#xff09;设置透明相机 1,透明相机的优点 透明相机机制更符合于人的视角,在场景预览和游戏场景多有使用…...

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…...

业务系统对接大模型的基础方案:架构设计与关键步骤

业务系统对接大模型&#xff1a;架构设计与关键步骤 在当今数字化转型的浪潮中&#xff0c;大语言模型&#xff08;LLM&#xff09;已成为企业提升业务效率和创新能力的关键技术之一。将大模型集成到业务系统中&#xff0c;不仅可以优化用户体验&#xff0c;还能为业务决策提供…...

C++:std::is_convertible

C++标志库中提供is_convertible,可以测试一种类型是否可以转换为另一只类型: template <class From, class To> struct is_convertible; 使用举例: #include <iostream> #include <string>using namespace std;struct A { }; struct B : A { };int main…...

django filter 统计数量 按属性去重

在Django中&#xff0c;如果你想要根据某个属性对查询集进行去重并统计数量&#xff0c;你可以使用values()方法配合annotate()方法来实现。这里有两种常见的方法来完成这个需求&#xff1a; 方法1&#xff1a;使用annotate()和Count 假设你有一个模型Item&#xff0c;并且你想…...

Frozen-Flask :将 Flask 应用“冻结”为静态文件

Frozen-Flask 是一个用于将 Flask 应用“冻结”为静态文件的 Python 扩展。它的核心用途是&#xff1a;将一个 Flask Web 应用生成成纯静态 HTML 文件&#xff0c;从而可以部署到静态网站托管服务上&#xff0c;如 GitHub Pages、Netlify 或任何支持静态文件的网站服务器。 &am…...

基于Docker Compose部署Java微服务项目

一. 创建根项目 根项目&#xff08;父项目&#xff09;主要用于依赖管理 一些需要注意的点&#xff1a; 打包方式需要为 pom<modules>里需要注册子模块不要引入maven的打包插件&#xff0c;否则打包时会出问题 <?xml version"1.0" encoding"UTF-8…...

QT: `long long` 类型转换为 `QString` 2025.6.5

在 Qt 中&#xff0c;将 long long 类型转换为 QString 可以通过以下两种常用方法实现&#xff1a; 方法 1&#xff1a;使用 QString::number() 直接调用 QString 的静态方法 number()&#xff0c;将数值转换为字符串&#xff1a; long long value 1234567890123456789LL; …...

鸿蒙DevEco Studio HarmonyOS 5跑酷小游戏实现指南

1. 项目概述 本跑酷小游戏基于鸿蒙HarmonyOS 5开发&#xff0c;使用DevEco Studio作为开发工具&#xff0c;采用Java语言实现&#xff0c;包含角色控制、障碍物生成和分数计算系统。 2. 项目结构 /src/main/java/com/example/runner/├── MainAbilitySlice.java // 主界…...

Yolov8 目标检测蒸馏学习记录

yolov8系列模型蒸馏基本流程&#xff0c;代码下载&#xff1a;这里本人提交了一个demo:djdll/Yolov8_Distillation: Yolov8轻量化_蒸馏代码实现 在轻量化模型设计中&#xff0c;**知识蒸馏&#xff08;Knowledge Distillation&#xff09;**被广泛应用&#xff0c;作为提升模型…...

return this;返回的是谁

一个审批系统的示例来演示责任链模式的实现。假设公司需要处理不同金额的采购申请&#xff0c;不同级别的经理有不同的审批权限&#xff1a; // 抽象处理者&#xff1a;审批者 abstract class Approver {protected Approver successor; // 下一个处理者// 设置下一个处理者pub…...