浏览器详解(四) 渲染
大家好,我是半虹,这篇文章来讲浏览器渲染
1、基本介绍
浏览器是多进程多线程的架构,包括有浏览器进程、渲染器进程、GPU
进程、插件进程等
在上篇文章中我们介绍过浏览器进程,作为浏览器主进程,负责浏览器基本界面的交互和底层功能的执行
在这篇文章中我们来介绍渲染器进程,这是浏览器的内核,也称为渲染进程
渲染进程负责标签页内所发生的事情,主要包括页面渲染、脚本执行、事件处理等
其中包含多个重要的线程以协同工作:
-
GUI
渲染线程:负责页面的渲染,解析 HTML、CSS,构建 DOM、CSSOM,布局和绘制等如果页面要回流或重绘,也由该线程去执行
-
JS
引擎线程:负责解析和执行 JavaScript 脚本,注意JS
引擎线程与GUI
渲染线程互斥 -
事件触发线程: 控制事件循环,并管理事件队列
若当
JS
引擎执行触发异步事件,会将对应的任务交给相应的线程执行处理等到任务完成之后,把任务对应的回调添加到事件队列,等待
JS
引擎处理 -
定时触发线程: 负责处理特定任务,具体来说就是用于
setTimeout
和setInterval
计时等到计时结束,通知事件触发线程,由事件触发线程将对应的回调加入队列
-
异步请求线程: 负责处理特定任务,具体来说就是处理
AJAX
请求等到请求完成,通知事件触发线程,由事件触发线程将对应的回调加入队列
下面我们通过例子,异步请求和定时操作,来说明线程之间如何协同工作

上述多个线程大体上可以分成两组:
- 渲染引擎:其中核心为
GUI
渲染线程 - 脚本引擎:其中核心为
JS
引擎线程,辅助为事件触发线程、定时触发线程、异步请求线程
目前市面主流浏览器使用的渲染引擎和脚本引擎总结如下:
浏览器 | 渲染引擎 | 脚本引擎 |
---|---|---|
Safari | Webkit / WebCore | Webkit / JavaScriptCore |
Chrome | Blink | V8 |
Firefox | Gecko | SpiderMonkey → TraceMonkey → JaegerMonkey |
IE | Trident | Chakra |
Edge | EdgeHTML | Chakra |
在这篇文章中我们主要介绍渲染过程,至于 JS
的解析过程,以后有空的话会专门再讲
好了,下面来正式开始介绍浏览器中的渲染过程
2、渲染流程
渲染引擎渲染页面的过程,也称为关键渲染路径,具体步骤如下:
-
解析 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 阻塞时,预加载扫描器也能继续请求资源
-
解析 CSS 构建 CSSOM
具体步骤如下:
搜集:从内联样式、内部样式表、外部样式表、浏览器代理样式中,搜集所有样式规则
构建:按上述规则构建 CSSOM 树状结构,其中每个节点表示一个样式规则,包含选择器以及样式声明
匹配:遍历 DOM 节点匹配选择器,并为匹配的节点执行样式设置
计算:计算 DOM 节点的最终样式,这些样式可通过以下接口查看:
window.getComputedStyle()
需要注意的是:
上述匹配步骤,CSS 选择器会从右往左匹配 DOM 节点,举个例子,假设 CSS 选择器为
.nav span
那么匹配之时,首先找到所有
span
节点,然后对每个span
节点,向上找.nav
节点
另外注意的是:
构建 DOM 和构建 CSSOM 是同时进行的,但是这并不意味着二者不会相互影响
因为 CSSOM 构建会阻塞脚本执行,而脚本执行会阻塞 DOM 构建,有一个间接的影响
-
布局 (Layout)
有了 DOM 和 CSSOM 后,需要根据二者构建出渲染树 (RenderObject Tree)
渲染树只会包含可见元素,像 head 节点和 display 为 none 的节点,则不会出现在渲染树中
至此,其实已经知道每个可见节点的样式,接下来需要根据渲染树进行布局计算
即根据盒子模型和视觉格式化模型来计算每个节点在设备视口内的位置以及大小
当页面布局和几何信息发生变化时,浏览器需要重新布局,这个过程被称为回流 (reflow)
具体又可分为全局布局和局部布局,常见的引起回流阶段的操作如下:
- 页面第一次进行渲染
- 窗口的尺寸发生改变
- 增加或删除可见元素
- 元素的位置发生改变
- 元素的大小发生改变
- …
-
绘制 (Paint)
绘制实际上就是遍历渲染树,在屏幕中绘制出对应的节点内容,简单来说就是个像素填充的过程
绘制过程在多个层上完成的,这些层被称为渲染层 (Render Layer)
渲染树中每个节点都是一个渲染对象,处于相同坐标空间 (z 轴) 的渲染对象会合并到同一个渲染层
对于满足层叠上下文条件的渲染对象,浏览器会为其自动创建新的渲染层,常见情况如下:
- 根元素
- 有
mask
属性 - 有
filter
属性 - 有
reflection
属性 - 有
position
属性且值为absolute
、relative
、fixed
、sticky
- 有
backface-visibility
属性且值为hidden
- 有
transform
属性且值不为none
- 有
overflow
属性且值不为visible
- 有
opacity
属性且值小于1
- 对
opacity
、transform
、filter
等属性应用动画
满足上述条件的渲染对象拥有独立的渲染层,但这并不意味着这些渲染对象独享该渲染层
这是因为不满足上述条件的渲染对象将会与第一个拥有渲染层的父元素共用同一个渲染层
当元素样式有变化但不影响布局时,浏览器需要重新绘制,这个过程被称为重绘 (repaint)
具体又可分为全局绘制和局部绘制,常见的引起重绘阶段的操作如下:
- 修改元素
color
- 修改元素
background-color
- …
值得注意的是:回流一定引起重绘,重绘不一定引起回流
-
合成 (Composite)
合成阶段中,多个渲染层按照恰当的重叠顺序进行合并,而后生成位图,最终通过显卡展示在屏幕上
在这过程中,某些渲染层会被浏览器自动提升为合成层 (Compositing Layer), 常见情况如下:
- 有
will-change
属性 - 有
position
属性且值为fixed
- 有
transform
属性且值为translate3d
、translateZ
等 3D 变换 - 是
canvas
、video
、iframe
等元素 - 对
opacity
、transform
、filter
等属性应用动画
每个合成层拥有单独的 Graphics Layer 用于生成位图,并上传到 GPU 对其进行合成,绘制到屏幕上
其它不是合成层的渲染层与第一个拥有 Graphics Layer 的父层级共用
合成层有什么用?答案是硬件加速,即把某些需要频繁回流和重绘的元素单独提升成为合成层
这样不仅可以提高绘制的效率,也能减少这些元素修改时对其它元素影响
例如修改合成层的
transform
时,不会带来回流或重绘,只需要将多个图层再次合并,而后生成位图这样我们可以通过
transform:translate
修改元素位置,通过transform:scale
修改元素大小 - 有
渲染进程的工作对浏览器性能有重要影响,常见渲染优化包括:
- 关键样式资源放在头部加载,善用
preload
、prefetch
- 脚本资源通常放在脚部加载,善用
async
、defer
- 尽量减少回流以及重绘操作,例如 批量修改 DOM 、离线修改 DOM 等等
- 对于画布,常用的技术包括有分层 canvas、离屏 canvas 等等
- 对于动画,使用
requireAnimationFrame
代替setTimeout
- 硬件加速
- …
类似方法有很多,这些都需要在日常开发中不断地积累,并有意识地应用到未来开发中
好啦,本文到此结束,感谢您的阅读!
如果你觉得这篇文章有需要修改完善的地方,欢迎在评论区留下你宝贵的意见或者建议
如果你觉得这篇文章还不错的话,欢迎点赞、收藏、关注,你的支持是对我最大的鼓励 (/ω\)
相关文章:

浏览器详解(四) 渲染
大家好,我是半虹,这篇文章来讲浏览器渲染 1、基本介绍 浏览器是多进程多线程的架构,包括有浏览器进程、渲染器进程、GPU 进程、插件进程等 在上篇文章中我们介绍过浏览器进程,作为浏览器主进程,负责浏览器基本界面的…...

idea新建一个module时,文件夹显示灰色/pom.xml文件显示灰色且中间有条横线
1.问题 2.解决方法 File->Settings->Ignored Files->找到勾选的pom.xml文件,取消勾选,点击ok即可。 3.已解决...

NoSQL数据库(林子雨慕课课程)
文章目录 5.1 NoSQL数据库5.2 NoSQL和关系数据库的比较5.3 四大类型NoSQL数据库5.3.1 键值数据库和列族数据库5.3.2 文档数据库、图数据库、以及不同数据库比较分析 5.4 NoSQL数据库的理论基石CAP理论:BASE理论:Eventual consistency(最终一致…...

模拟器运行在AndroidStudio内部,设置其独立窗口显示
在窗口内部运行 设置成独立窗口 Android Studio->Settings或Preferences->Tools->Emulator->取消勾选Launch in the Running Devices tool window --->点击右下角的OK按钮 ---> 重启Android Studio 再次启动模拟器...

计算机网络 | 体系结构
计算机网络 | 体系结构 计算机网络 | 体系结构概念及功能计算机网络简介计算机网络的功能因特网发展阶段小结 组成与分类计算机网络的组成计算机网络的分类小结 标准化工作及相关组织速率相关性能指标速率带宽吞吐量小结 时延相关性能指标时延时延带宽积往返时延RTT利用率小结 …...

ELK 处理 SpringCloud 日志
在排查线上异常的过程中,查询日志总是必不可缺的一部分。现今大多采用的微服务架构,日志被分散在不同的机器上,使得日志的查询变得异常困难。工欲善其事,必先利其器。如果此时有一个统一的实时日志分析平台,那可谓是雪…...

mac使用python递归删除文件夹下所有的.DS_Store文件
import osfolder_path "yourself file path"for root, dirs, files in os.walk(folder_path):for filename in files:if filename .DS_Store:file_path os.path.join(root, filename)os.remove(file_path)print("delete ok")...

Gitlab+Jenkins自动化部署,解放双手
项目打包 在部署项目前需要对源码进行打包,一个简单的SpringBoot项目默认是打包为jar包,也就是在pom.xml中的<packaging>jar</packaging>方式,当然也会有一些打包成war包方式,使用外置的Tomcat应用服务器部署war包…...
NNDL:作业3
在Softmax回归的风险函数(公式(3.39))中如果加上正则化项会有什么影响? (1) 在 Softmax 回归的风险函数中加入正则化项会对模型的训练产生影响。正则化项的作用是对模型的复杂度进行惩罚,防止过拟合的发生。 (2) 原书公式为: 在加入正则化后损失函数…...

dockers --cap-add 哪些值可以设置
--cap-add 参数可以用于向 Docker 容器添加不同的权限。除了 NET_ADMIN,还有一些其他常用的权限值,包括: SYS_ADMIN:添加系统管理员权限,允许容器内的进程执行系统级别的管理操作,如挂载文件系统、设置时间…...

golang常用库之-HTTP客户端请求库 grequests
文章目录 golang常用库之-HTTP客户端请求库 grequests什么是grequests使用 golang常用库之-HTTP客户端请求库 grequests 什么是grequests 官网:github.com/levigross/grequests A Go “clone” of the great and famous Requests library Go语言的grequests库是一…...

17基于matlab卡尔曼滤波的行人跟踪算法,并给出算法估计误差结果,判断算法的跟踪精确性,程序已调通,可直接运行,基于MATLAB平台,可直接拍下。
17基于matlab卡尔曼滤波的行人跟踪算法,并给出算法估计误差结果,判断算法的跟踪精确性,程序已调通,可直接运行,基于MATLAB平台,可直接拍下。 17matlab卡尔曼滤波行人跟踪 (xiaohongshu.com)...

SpringCloud之Stream框架集成RocketMQ消息中间件
Spring Cloud Stream 是一个用来为微服务应用构建消息驱动能力的框架。它可以基于 Spring Boot 来创建独立的、可用于生产的 Spring 应用程序。Spring Cloud Stream 为一些供应商的消息中间件产品提供了个性化的自动化配置实现,并引入了发布-订阅、消费组、分区这三…...

与创新者同行!Apache Doris 首届线下峰会即将开启,最新议程公开!|即刻预约
点击此处 即刻报名 Doris Summit Asia 2023 回顾人类的发展史,地球起源于 46 亿年前的原始星云、地球生命最初出现于 35 亿年前的原始海洋、人类物种诞生于数百万年前,而人类生产力的真正提升源于十八世纪六十年代的工业革命,自此以后&#…...

vue解决:Parsing error: No Babel config file detected for ....
报错信息 Parsing error: No Babel config file detected for C:\Users\Admin\Desktop\shabi\work\src\App.vue. Either disable config file checking with requireConfigFile: false, or configure Babel so that it can find the config files. 分析错误:没有检测…...

算法题:K 次取反后最大化的数组和(典型的贪心算法问题)
这道题没有看题解,直接提交,成绩超越99.5%,说明思路是优的。就是考虑的情况里面弯弯绕比较多,需要考虑全面一点。(本题完整题目附在了最后面) 具体思路如下: 1、首先排序,然后从最…...

Go语言中向[]byte数组中增加一个元素
要向http.Request的body中添加一个键值对,可以先将其转换为一个map,然后对其进行修改,最后再将其转回为byte数组。 以下是一个示例代码: import ("net/http""io/ioutil""encoding/json" )type Re…...

CSS 布局案例: 2行、多行每行格数不定,最后一列对齐
布局期望的效果如下: 第二行最后一格与第一行最后一格对齐。每行格数不定。自动拉伸填充整个宽度 实现: 一开始打算用display:flex, 自动分散,但是第二行对齐第一行最后一格控制不了。 使用grid fr均分单位控制。 <!DOCTYPE…...

数据结构--算法、数据结构的基本概念
📕参考:王道 一、算法的基本概念 1.程序数据结构算法 2.算法的特性 (1)有穷性 执行有穷步之后结束,且每一步都可在有穷时间内完成。 (2)确定性 (3)可行性 可通过已经实…...

Edge浏览器下载文件被保存为 .crdownload 文件的问题小记
问题 近期使用Edge浏览器下载文件时,文件都被保存为 .crdownload 格式的文件了,不确定从哪个版本开始的。除非下载未完成导致文件不完整,否则不会被保存为 .crdownload 格式的文件;实际上文件已完成了下载,且手工修改…...

6-10 单链表分段逆转 分数 15
void K_Reverse( List L, int K ) { //此题已经默认size > K 因为当size < K时 反转后将不再符合链表的定义//求出表中元素个数int size 0;for (List cur L->Next; cur ! NULL; cur cur->Next)size; List prv, cur, next, first, head L;//共需要反转 si…...

【单片机】17-温度传感器DS18B20
1.DS18B20相关背景知识 1.温度传感器 (1)测温度的方式:物理(汞柱,气压),电子(金属电性能随温度变化) (2)早期:热敏电阻(模…...

力扣 -- 5. 最长回文子串
解题步骤: 参考代码: class Solution { public:string longestPalindrome(string s) {int ns.size();vector<vector<bool>> dp(n,vector<bool>(n));//最长回文串的起始位置int start0;//最长回文串的长度int len0;for(int in-1;i>…...

SpringCloud源码探析(十)-Web消息推送
1.概述 消息推送在日常使用中的场景比较多,比如有人点赞了我的博客或者关注了我,这时我就会收到一条推送消息,以此来吸引我点击或者打开应用。消息推送的方式主要分为两种:web消息推送和移动端消息推送。它将所要发送的信息&…...

Vue、React和小程序中的组件通信:父传子和子传父
在前端开发中,组件化是一种常见的开发模式,它可以将复杂的用户界面拆分成多个可重用的组件。在Vue、React和小程序中,组件之间的数据和事件传递是非常关键的,其中父传子和子传父是常见的通信方式。本文将介绍在Vue、React和小程序…...

安卓玩机----展讯芯片机型解锁 读写分区工具 操作步骤解析
国内机型大都使用高通和MTK芯片。展讯芯片使用的较少。相对来说高通和mtk机型解锁以及读取分区工具较多。展讯的几乎没有。目前有大佬开发出了一款展讯芯片解锁 与读写分区工具.开源的tools 官方分享说明: 是一款专为 Windows 计算机设计的免费、用户友好的工具&am…...

微软放大招!Bing支持DALL-E3,免费AI绘画等你来体验!
最近 OpenAI 发布了DALL-E3模型,出图效果和Midjourney不相上下,不过要使用它有些门槛,必须是 ChatGPT Plus 账户,而且还要排队,怎么等都等不到,搞得大家都比较焦虑。 不过现在微软在Bing上也支持 DALL-E3 …...

tp5访问的时候必须加index.php,TP5配置隐藏入口index.php文件
PS:这里说的入口文件指的是public/index.php,配置文件就在这个目录下 可以去掉URL地址里面的入口文件index.php,但是需要额外配置WEB服务器的重写规则。 以Apache为例,需要在入口文件的同级添加.htaccess文件(官方默认自带了该文件)&#x…...

16k面试中的10个问题
你好,我是田哥 节前,有位朋友跟我反馈面试中一些问题,这位朋友的基本情况: 坐标:上海,年限:3年不到,期望薪资;16k 下面我们来看看具体问题: 01:请…...

STM32单片机入门学习(六)-光敏传感器控制LED
光敏传感器模块和LED接线 LED负极接B12,正极接VCC 光敏传感模块一DO端接B13,GND接GND,VCC接VCC,AO不接。 如图: 主程序代码:main.c #include "stm32f10x.h" #include "Delay.h" //delay函数所在头文件 #include …...