浏览器详解(四) 渲染

大家好,我是半虹,这篇文章来讲浏览器渲染


1、基本介绍

浏览器是多进程多线程的架构,包括有浏览器进程、渲染器进程、GPU 进程、插件进程等

在上篇文章中我们介绍过浏览器进程,作为浏览器主进程,负责浏览器基本界面的交互和底层功能的执行

在这篇文章中我们来介绍渲染器进程,这是浏览器的内核,也称为渲染进程


渲染进程负责标签页内所发生的事情,主要包括页面渲染、脚本执行、事件处理等

其中包含多个重要的线程以协同工作:

  1. GUI 渲染线程:负责页面的渲染,解析 HTML、CSS,构建 DOM、CSSOM,布局和绘制等

            如果页面要回流或重绘,也由该线程去执行

  2. JS  引擎线程:负责解析和执行 JavaScript 脚本,注意  JS 引擎线程与 GUI 渲染线程互斥

  3. 事件触发线程: 控制事件循环,并管理事件队列

            若当 JS 引擎执行触发异步事件,会将对应的任务交给相应的线程执行处理

            等到任务完成之后,把任务对应的回调添加到事件队列,等待 JS 引擎处理

  4. 定时触发线程: 负责处理特定任务,具体来说就是用于 setTimeoutsetInterval 计时

            等到计时结束,通知事件触发线程,由事件触发线程将对应的回调加入队列

  5. 异步请求线程: 负责处理特定任务,具体来说就是处理 AJAX 请求

            等到请求完成,通知事件触发线程,由事件触发线程将对应的回调加入队列


下面我们通过例子,异步请求和定时操作,来说明线程之间如何协同工作

线程协作

上述多个线程大体上可以分成两组

  1. 渲染引擎:其中核心为 GUI 渲染线程
  2. 脚本引擎:其中核心为  JS  引擎线程,辅助为事件触发线程、定时触发线程、异步请求线程

目前市面主流浏览器使用的渲染引擎和脚本引擎总结如下:

浏览器渲染引擎脚本引擎
SafariWebkit / WebCoreWebkit / JavaScriptCore
ChromeBlinkV8
FirefoxGeckoSpiderMonkeyTraceMonkeyJaegerMonkey
IETridentChakra
EdgeEdgeHTMLChakra

在这篇文章中我们主要介绍渲染过程,至于 JS 的解析过程,以后有空的话会专门再讲

好了,下面来正式开始介绍浏览器中的渲染过程


2、渲染流程

渲染引擎渲染页面的过程,也称为关键渲染路径,具体步骤如下:

  1. 解析 HTML 构建 DOM

    具体步骤如下:

    转换:从网络或磁盘读取 HTML 文件原始字节,根据文件编码将字节转换为字符串

    分词:根据 HTML 规范,将上述字符串切分为不同的标记,如 <html><body>

    语法 分析:将上述标记转换为对象,对象中包含语法信息,如属性、属性值等信息

    DOM 构建:将上述对象链接在树状的结构中以标识父子和兄弟关系,这个树状结构就是 DOM


    需要注意的是:

    在读取 HTML 时,DOM 的构建已经开始,可以将上面的步骤理解成是流水线并行

    上一个步骤每完成一点,就会将结果传递给下一个步骤,不会等一个步骤完全执行完才进行下一个步骤


    关于脚本资源

    当 HTML 解析器遇到 <script> 标签时,会暂停 HTML 的解析,并开始加载、解析和执行 JavaScript

    这是因为  JavaScript 可能会修改 DOM 的结构,若明确  JavaScript  不会修改 DOM, 那么:

    你可以为  <script> 标签加 async  属性,这样是异步加载 JavaScript,但是加载完马上就会开始执行

    也可以为  <script> 标签加 defer  属性,这也是异步加载 JavaScript,且等 DOM 构建完才开始执行

    线程协作

    其实如果  JavaScript 确实要修改 DOM 某节点,那也该将其放到 DOM 的构建之后

    否则有些获取元素的 操作可能就不能生效


    关于外部资源

    一个网页通常包含多个外部资源,例如图片、CSS、JavaScript、字体等等

    在解析 DOM 过程中,会按文档的顺序逐一发起请求 ,但是为了提高效率会同时运行预加载扫描器

    预加载扫描器查看已切分的标记,从中寻找资源地址并告知网络线程请求

    如此当 HTML解析被 JavaScript 阻塞时,预加载扫描器也能继续请求资源


  2. 解析 CSS 构建 CSSOM

    具体步骤如下:

    搜集:从内联样式、内部样式表、外部样式表、浏览器代理样式中,搜集所有样式规则

    构建:按上述规则构建 CSSOM 树状结构,其中每个节点表示一个样式规则,包含选择器以及样式声明

    匹配:遍历 DOM 节点匹配选择器,并为匹配的节点执行样式设置

    计算:计算 DOM 节点的最终样式,这些样式可通过以下接口查看:window.getComputedStyle()


    需要注意的是:

    上述匹配步骤,CSS 选择器会从右往左匹配 DOM 节点,举个例子,假设 CSS 选择器为 .nav span

    那么匹配之时,首先找到所有 span 节点,然后对每个 span 节点,向上找 .nav 节点


    另外注意的是:

    构建 DOM 和构建 CSSOM 是同时进行的,但是这并不意味着二者不会相互影响

    因为 CSSOM 构建会阻塞脚本执行,而脚本执行会阻塞 DOM 构建,有一个间接的影响

    线程协作
  3. 布局 (Layout)

    有了 DOM 和 CSSOM 后,需要根据二者构建出渲染树 (RenderObject Tree)

    渲染树只会包含可见元素,像 head 节点和 display 为 none 的节点,则不会出现在渲染树中


    至此,其实已经知道每个可见节点的样式,接下来需要根据渲染树进行布局计算

    即根据盒子模型和视觉格式化模型来计算每个节点在设备视口内的位置以及大小


    当页面布局和几何信息发生变化时,浏览器需要重新布局,这个过程被称为回流 (reflow)

    具体又可分为全局布局和局部布局,常见的引起回流阶段的操作如下:

    • 页面第一次进行渲染
    • 窗口的尺寸发生改变
    • 增加或删除可见元素
    • 元素的位置发生改变
    • 元素的大小发生改变

  4. 绘制 (Paint)

    绘制实际上就是遍历渲染树,在屏幕中绘制出对应的节点内容,简单来说就是个像素填充的过程

    绘制过程在多个层上完成的,这些层被称为渲染层 (Render Layer)


    渲染树中每个节点都是一个渲染对象,处于相同坐标空间 (z 轴) 的渲染对象会合并到同一个渲染层

    对于满足层叠上下文条件的渲染对象,浏览器会为其自动创建新的渲染层,常见情况如下:

    • 根元素
    • mask 属性
    • filter 属性
    • reflection 属性
    • position 属性且值为 absoluterelativefixedsticky
    • backface-visibility 属性且值为 hidden
    • transform 属性且值不为 none
    • overflow 属性且值不为 visible
    • opacity 属性且值小于 1
    • opacitytransformfilter 等属性应用动画

    满足上述条件的渲染对象拥有独立的渲染层,但这并不意味着这些渲染对象独享该渲染层

    这是因为不满足上述条件的渲染对象将会与第一个拥有渲染层的父元素共用同一个渲染层


    当元素样式有变化但不影响布局时,浏览器需要重新绘制,这个过程被称为重绘 (repaint)

    具体又可分为全局绘制和局部绘制,常见的引起重绘阶段的操作如下:

    • 修改元素 color
    • 修改元素 background-color

    值得注意的是:回流一定引起重绘,重绘不一定引起回流


  5. 合成 (Composite)

    合成阶段中,多个渲染层按照恰当的重叠顺序进行合并,而后生成位图,最终通过显卡展示在屏幕上

    在这过程中,某些渲染层会被浏览器自动提升为合成层 (Compositing Layer), 常见情况如下:

    • will-change 属性
    • position 属性且值为 fixed
    • transform 属性且值为 translate3dtranslateZ 等 3D 变换
    • canvasvideoiframe 等元素
    • opacitytransformfilter 等属性应用动画

    每个合成层拥有单独的 Graphics Layer 用于生成位图,并上传到 GPU 对其进行合成,绘制到屏幕上

    其它不是合成层的渲染层与第一个拥有 Graphics Layer 的父层级共用


    合成层有什么用?答案是硬件加速,即把某些需要频繁回流和重绘的元素单独提升成为合成层

    这样不仅可以提高绘制的效率,也能减少这些元素修改时对其它元素影响

    例如修改合成层的 transform  时,不会带来回流或重绘,只需要将多个图层再次合并,而后生成位图

    这样我们可以通过 transform:translate 修改元素位置,通过 transform:scale 修改元素大小


渲染进程的工作对浏览器性能有重要影响,常见渲染优化包括:

  1. 关键样式资源放在头部加载,善用 preloadprefetch
  2. 脚本资源通常放在脚部加载,善用 asyncdefer
  3. 尽量减少回流以及重绘操作,例如 批量修改 DOM 、离线修改 DOM 等等
  4. 对于画布,常用的技术包括有分层 canvas、离屏 canvas 等等
  5. 对于动画,使用 requireAnimationFrame 代替 setTimeout
  6. 硬件加速

类似方法有很多,这些都需要在日常开发中不断地积累,并有意识地应用到未来开发中



好啦,本文到此结束,感谢您的阅读!

如果你觉得这篇文章有需要修改完善的地方,欢迎在评论区留下你宝贵的意见或者建议

如果你觉得这篇文章还不错的话,欢迎点赞、收藏、关注,你的支持是对我最大的鼓励 (/ω\)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/100349.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

超低延时直播技术演进之路-进化篇

一、概述 网络基础设施升级、音视频传输技术迭代、WebRTC 开源等因素&#xff0c;驱动音视频服务时延逐渐降低&#xff0c;使超低延时直播技术成为炙手可热的研究方向。实时音视频业务在消费互联网领域蓬勃发展&#xff0c;并逐渐向产业互联网领域加速渗透。经历了行业第一轮的…

Doris 2.0.1 DockerFile版 升级实战

1、Doris 2.0.1 DockerFile 的制作 参考 Doris 2.0.1 Dockerfile制作-CSDN博客 2、之前的Doris 集群通过 Docker容器进行的部署&#xff0c;需提前准备好Doris2.0.1的镜像包 参考&#xff1a; 集群升级 - Apache Doris Doris 升级请遵守不要跨两个及以上关键节点版本升级的…

Jenkins 构建时动态获取参数

文章目录 问题简介Groovy 脚本配置进阶 问题 在做jenkins项目时&#xff0c;有些参数不是固定写死的&#xff0c;而是动态变化的&#xff0c;这时我们可以用 Active Choices 插件来远程调用参数 问题解决方案&#xff1a;执行构建前使用Groovy Scrip调用本地脚本&#xff0c;…

点云处理开发测试题目 完整解决方案

点云处理开发测试题目 文件夹中有一个场景的三块点云数据,单位mm。是一个桌子上放了一个纸箱,纸箱上有四个圆孔。需要做的内容是: 1. 绘制出最小外接立方体,得到纸箱的长宽高值。注意高度计算是纸箱平面到桌子平面的距离。 2. 计算出纸箱上的四个圆的圆心坐标和半径,对圆…

论文解析——AMD EPYC和Ryzen处理器系列的开创性的chiplet技术和设计

ISCA 2021 摘要 本文详细解释了推动AMD使用chiplet技术的挑战&#xff0c;产品开发的技术方案&#xff0c;以及如何将chiplet技术从单处理器扩展到多个产品系列。 正文 这些年在将SoC划分成多个die方面有一系列研究&#xff0c;MCM的概念也在不断更新&#xff0c;AMD吸收了…

Git基础使用

Git基础使用 1、git的本质2 Gitlab账号申请、免密设置2.1 申请Gitlab账号2.2 免密设置2.2.1 公钥及私钥路径2.2.2 免密设置 3、常用命令3.1 git全局配置信息3.2 初始化项目3.3 拉取项目 将日常笔记记录上传&#xff0c;方便日常使用翻阅。 1、git的本质 git对待数据更像是一个快…

【jvm--堆】

文章目录 1. 堆&#xff08;Heap&#xff09;的核心概述2. 图解对象分配过程2.1 Minor GC&#xff0c;MajorGC、Full GC2.1 堆空间分代思想2.3 内存分配策略2.4 TLAB&#xff08;Thread Local Allocation Buffer&#xff09;2.5 堆空间的参数设置2.6 逃逸分析2.7 逃逸分析&…

MODBUS-RTU通信协议功能码+数据帧解读(博途PLC梯形图代码)

MODBUS通信详细代码编写,请查看下面相关链接,这篇博客主要和大家介绍MODBUS协议的一些常用功能码和具体数据帧解析,以便大家更好的理解MODBUS通信和解决现场实际问题。 S7-1200PLC MODBUS-RTU通信 博途PLC 1200/1500PLC MODBUS-RTU通讯_博图modbus rtu通讯实例-CSDN博客1、…

【centos7安装ElasticSearch】

概述 最近工作中有用到ES &#xff0c;当然少不了自己装一个服务器捣鼓。本文的ElasticSearch 的版本&#xff1a; 7.17.3 一、下载 ElasticSearch 点此下载 下载完成后上传至 Linux 服务器&#xff0c;本文演示放在&#xff1a; /root/ 下&#xff0c;进行解压&#xff1…

Go 复合类型之字典类型介绍

Go 复合类型之字典类型介绍 文章目录 Go 复合类型之字典类型介绍一、map类型介绍1.1 什么是 map 类型&#xff1f;1.2 map 类型特性 二.map 变量的声明和初始化2.1 方法一&#xff1a;使用 make 函数声明和初始化&#xff08;推荐&#xff09;2.2 方法二&#xff1a;使用复合字…

边坡安全监测系统的功能优势

随着科技的进步&#xff0c;边坡安全监测系统在各种工程项目中发挥着越来越重要的作用。这款系统通过实时监测垂直、水平位移数据&#xff0c;以折线图的方式显示在监控平台中&#xff0c;为工程人员提供了直观、便捷的监控工具&#xff0c;从而能够及时掌握边坡稳定状况&#…

Gin 文件上传操作(单/多文件操作)

参考地址: 单文件 | Gin Web Framework (gin-gonic.com)https://gin-gonic.com/zh-cn/docs/examples/upload-file/single-file/ 单文件 官方案例: func main() {router := gin.Default()// 为 multipart forms 设置较低的内存限制 (默认是 32 MiB)router.MaxMultipartMem…

【软件测试】JUnit详解

文章目录 一. Junit是什么?二.Junit中常见的注解1. Test2. BeforeAll & AfterAll3. BeforeEach & AfterEach4. ParameterizedTest参数化5. Disabled6. Order 三. 测试套件1. 通过class运行测试用例2. 通过包运行测试用例 四. 断言 一. Junit是什么? JUnit是一个用于…

C#练习题-构造函数

文章目录 前言题目习题1运行示例 习题2运行示例 参考答案习题1习题2 其他文章 前言 本篇文章的题目为C#的基础练习题&#xff0c;构造函数部分。做这些习题之前&#xff0c;你需要确保已经学习了构造函数的知识。 本篇文章可以用来在学完构造函数后加深印象&#xff0c;也可以…

探馆天津车展 近距离感受“极致性能王”远航汽车

近年来&#xff0c;新能源汽车产业发展迅猛。得益于新能源车型在成本控制、品质、安全性等多方面的出色表现&#xff0c;消费者对新能源汽车的需求一直呈现刚性。2023年&#xff0c;虽然新能源汽车已经进入无补贴时代&#xff0c;但消费者对新能源汽车的需求依旧有增无减&#…

k8s安全机制

安全机制 一、机制说明二、认证&#xff08;Authentication&#xff09;HTTP Token 认证HTTP Base 认证HTTPS 证书认证&#xff08;最严格&#xff09; 三、鉴权&#xff08;Authorization&#xff09;角色角色绑定主体&#xff08;subject&#xff09;Role and ClusterRoleRol…

axios的get请求时数组参数没有下标

开发新项目过程中 发现get请求时 数组参数没有下标 这样肯定是不行的 后端接口需要数组[0]: 7 数组[1]:4这样的数据 原因是因为在请求拦截器没有处理需要的参数 解决方法 在请求拦截器 处理一下参数 import axios, { AxiosError, AxiosInstance, AxiosRequestHeaders } fro…

vcf 文件如何修改染色体修改样本名称提取样本

大家好&#xff0c;我是邓飞。 对于vcf文件和plink文件是经常用的文件&#xff0c;对于基因型数据的处理&#xff0c;一般分为&#xff1a; 数据质控数据提取染色体修改名称样本修改名称 今天介绍一下vcf文件的三个处理方法&#xff1a; 1&#xff0c;染色体修改2&#xff…

4.Docker 搭建 redis6

1.下载redis docker pull redis:6.2.62.创建需要挂载的宿主机文件夹 mkdir -p /data/redis/conf mkdir -p /data/redis/data3.配置redis 切换到/data/redis/conf文件夹下&#xff0c;创建redis.conf,复制redis.conf配置文件内容到redis.conf文件中&#xff0c;然后按下键盘 …

竞赛 深度学习 opencv python 公式识别(图像识别 机器视觉)

文章目录 0 前言1 课题说明2 效果展示3 具体实现4 关键代码实现5 算法综合效果6 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 基于深度学习的数学公式识别算法实现 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c;学…