我们经常写 HTML 、 CSS 和 JavaScript ,写好这些之后,我们就会在浏览器中看到页面,那浏览器究竟在这背后做了一些什么事情呢?本篇文章将揭晓答案!
了解浏览器的r 9 O p g } . M Z渲染原理是我们在通往更深层次的前w ( M i _ ^ % ^ E端U ) p $ C b N开发中不可缺少的,它可以让我们从更深层次、角度k F g u F 6 ~ 3去考虑性能优化等~
下面进入正% S / M - W文~
进程、线程
浏览器会分配一个线程“自上而下,从左到右”依次解析和渲染代码,那么进程和线程是什= - V D : 9 h .么,它们之间有着怎样的关系呢?
进程
一个进程就是一个程序o n { { 6 - ,运行的实例。当启动K s a |一个程序的时候,操作系统会为该程序创建一块内存,用来存放代码,运行中的数据和一个执行任务的主线程,这样的一个运行环境就叫进程
线程
线程不能单独存在,它是由进程来启动和管理的。线程依附于进程,进程中使用多线程并行处理能提升运算效率
两者之间的关系
1、进程中的任意一线W ; l i b O程执行出错,都会导致整个z J V n进程的崩溃
2、线4 t m @ . :程之间可以共享B } / C V ]数据
3、当一个进程关闭后,操作系统r l W j J f n u会回收进程所占用的内存
渲染机制
从HTML、Q & 1 L L o 7 PCSS和JavaScript开始
了解浏览器的渲染原理,我们就要从理解; 7 P { t x k ( k HTML 、 CSS 和 JavaScrip 开始,我们先来看一张图
HTML (超文本标记语言),顾名? T 9 7 u _ 8 ^思义,由标记(标签)和文本组成,每个标签都有自己的1 k T q M y j s语意,浏览器会根据标签和文本展示对应的内容。
CSV q XS (层叠样式表),由选择器和属性组成c N p Z l,它可以改变 HTML 的样式,比如上图中,我们改变了4 / % C span 的颜色由蓝色为绿色。h ) ` H ! h 2 j ^
JavaScript ,我们可以通过 JS 完成很多事情,例如上图中修改样式。
下面开始分析渲染的原理
渲染流水线
渲染模块由于渲染的机制的复杂,被划分为了很多子阶段,输入的 HTML 经过这些子阶段,最后会输出为像素。这样的处理流程就= J e _ 叫做 渲染流水线
按照渲染` * u ) S的时间顺序,流水线可分为几个子阶段:构建 DOM 树、样式计算、布局阶段、分层、绘制、分块、光栅化和合成
构建DOM树
由于浏览器无法直接理解和使用 HTX P OML ,所以需要将 HTML 转换为浏览器能够理解的结构( DOM 树)
树结构示意图
DOM树3 D k ( 3 * E的构建过程
我们来分析一下下面这段代码会构建出一棵什么样的 DOM 树
我们先将上面的代码运行,然后在浏览器控制台输入 document ,看看会有什么效果
我们一层级一层级的打开就会看到g j如上图的效果,我们可以根据这每一层级展开的效果,绘制出一棵 DOM 树结构,i ; u如下:n [ D { h
接下来,我们试* { 9 / _一下利用 JS 修改一下内容,看有什么改变:
我们可以看到“浏览器”的文字变成了“chrome”
再来看一下 DOM 树是否有改变
我们看到在“浏览器”的位置换成了“chrome”,那么如何让 DOM 节点拥有样式?
样式计算
样式计算K ^ b D + k,顾名思义,就是 计算出 DOM 节点中每个元素的具体样式 ,这个阶段会分为三部分:
- 把 CSS 转换为浏览器能够理解的结构
- 转换样式表中的属性值,使其标准化
- 计算出 DOM 树中每个节点的样式
CSS样式来源
- link 导入外y B S F h @ F e e部样式资源
浏览器会新开辟k Z t 3一个线程,去服务器获取对应的资源文件(不阻碍主线程的渲染)
- style 内嵌样式
从上到下解析,解析完继续解析 DOM 结构。在真实项目中,如果 css 代码不是很多,或是移动端项目,我们应该使用内嵌式,以此来减少 http 资源的请求,提高; D M x V $ ; f 7页面渲染速度
- 行内样式
- @import 导入
它R K 0 Q b s W w是同步的,不会开辟新线程去加载资源文件,而是让主线程去获取,这阻碍 DOM 结构的继续渲染;只有把外部样式导入进来,并且解析后,才s k c U会继续渲染 DOM 结构
把CSS转换为浏览器能够理解的结构
浏览器就像不能理解 HTML 一样,不理解 CZ z _ b 9 q 8 : 4SS ,所以当渲染引擎接收到 CSS 文件时,会执行转换操作,将 CSS 文本转换为浏览器可以理解的 styleSheets 结构。e d T w v d $ s
在 HTML 中,在浏览器中输入 document 可以查看 html 的结构。在 css 中,可以输入 document.s0 8 Y ltyleSheets 看到 css 的结构
现在的D d h结构是空的,我们来加一% F ] X $些样式,U n J l t看看效果
转换样式表中D X V X v的属性值,使m Q T : w N其标准化
属性值标准化就是将所有值转换为渲染引擎容易理解的、标准化的计算值。我们大致看一下效果:
- 标准化= Z F q w m 3 ?前
body! . H Z 1 ~ ! L i {
font-size: 2em;
cx / # j J qolor: black;
font-weight: bold;
...
}
复制代码
- 标准化后
body {
font-size: 16px;
color: rgb(0, 0, 0);
font-wX C G ^ D Zeight: 700;
...
}
复制代码
计算出DOM树中每个节点的具体样式
样式计算有两个CSS的规则:继承规则和层叠规则
- CSS继承规则
CSU X F ) 7 = 7S 继承就是每个 DOM 节点都包含h ] ] B有父节点的样式。我们5 h c来看一下下面这段代码中如何应用到 DOM 节点上
<!DOCTYPE html>
<html lang=\"en\b x ; A p">
<head>
<meta charsp . L 5 Aet=\"UTF-8\">
<L { | m;title>Document</title>
<style>
h1 {
color: red;
}
div {
color: blue;
}
span {
font-size: 16px;
}
</style>
</head>
<body>
<h1>掘金</h1>
<div>
<span>浏览器</span>
<span>渲染原理</span>q l ? r L M
构建DOM树
</div>
</body>
</html>
复制代码
子节点会拥有父节点的样H : A g - a Z m n式,由此我们可以画出这样一张图
我们还可以打开控Z P I Y _ G - s u制台,看一下选中 span 标签,都会看到哪些内容
通过上图,我们可看到一个元素的样式、继承/ 4 U + $ #过程等, useg * { % rrAgent 样式是浏览器默认的内置样式,如果我们不提供任何样式,就会使用此样式。
- 样式层叠规则
层叠在 CSS 处于核心地位,它是 CSS 的一个基本特征,它定义了如何合并来自多个源的属性值的算法。
样式计算阶段最终输出的内容是每个 DOM 节点的样式,并且保存在了 ComputedStyle 中。我们可以通过控制台看到某个 DOM 元素最终的计算样式
布局阶段
现在我们不知道 DOM 元素的几何位置信息,所以现在我们需要计算出 DOM 树中可见元素的几何位置,这+ . c c a ( b 5个计算过程就叫做布局。布局阶段有两个过程:
- 创建布局树r 0 U v U W
- 布局计算
创建z G c 2布e ; h 0 w局树
创建布局树的意思就是创建一棵只包含可见元素的树。我们来看下面一段代码创建布局树的过程
<!DOCTYPE html>
<html lang=\"en\">
<head7 ( L>
<meta charset=\A w J / % L R &"UTF-8\">
<title>Document</title>
<style>
h1 {
color: red;
}
div {
color: blue;
}
div span {
font-size: 16px;
}
div span:last-child {= # A y ^ = P )
display: no~ G 3 0 , v m xne;
}
</style>
<2 ] ?;/head&Z 4 , h q Y )g} - Tt;
<k u J J b *body>
&lr D : y W x U ~ jt;h1>掘金</h1>
<div>
<span>浏览器</span>
<span>渲染原理</span>
构建D= L .OM树
&lp K 9 ot;/div>
</body>
</html>
复制代码
构建布局树的过程中, DOM 树中所有不可见的节点都不会包含在这棵树中。浏览器会遍历 DOM 树中所有能看见的节点,然后把这些节点? % y : = [加入到布局中;不可见的节点就会被忽略, head 标签下面的内容、 div 下最后一9 / ` ( M M / 6个 span 节点都不会在布局树中,我们看一下这个过程图感受一下~
布局计算
布局计算就是计算布局树节点的坐标位置。这个计算过程极为复杂。
分层
渲染引擎会为特定的节点生成专用的图层,并生成一棵对应的图层树。这样做是因为~ k 0 F + 9 2 j页面中可能含有很多复杂的效果,我们可以打p . X m b开控制台看一下页面的分层情况
我们可以看到,渲染引擎给页面分了很多图层,这些图层会按照一定顺= y I s L ( |序叠加在一起,形成最终的页面
那么图层的来源有哪些?
1、拥有层叠上下文, a F K Y s F属性的元素会被提升为单独的一层
层叠上下文可以使能够使 HTML 元素具. d 9 e o H m U有三维的概念,这些 HTML 元素按照自身属性的优先级分布在垂直于这个二维平面的 z 轴上。哪些元素具有层叠上下文属性?
2、需要剪裁的地方会被创建为图层
当我们创建一个有宽度和高度的 div 时,里面的文字内容可能会超出这个区域,这时候渲染引擎会把裁剪文字内容的一部分用于显示在 div 区域,例如
<!DOCTYPE html>
<html lang=\"en\">
<head>
<meta charset=\"UTF-8\">
<meta name=\"viewpo! w $ j i # s _ 6rt\"] n 3 b content=\"width=device-width, initial-scale=1.0\"&) T F t x H #gt;
<title>Document</title&] & 6 # l R ; G Rgt;
<style&g- ( D F 9 O v w qt;/ | J
div {
width: 100px;
height: 100px;
background: yellowj Y 5 U U 4;
overflow: auto;} n D _ Y m d
}
</style>
</head>
<body>
<div>
我们经常写`HTML`、`C} E M ` 2 *SS`和`JavaScript`,写好这些之后,我们就会在浏览器中看到页面,那浏览器究竟在这背后做了一些什么事情呢?本篇文章将揭晓% H I g p [ % [ p答案!
了解浏览器的渲染原理是我们在通往更深层G G T [次的前端开发中不可缺少的,它可以让我们从更深层次、角度去考虑性能优化等~
&[ K i T i / W Olt;/div>
</body>
<x , (;/html>
复制代码
运行结果
我们再打开控制台的 layers 看一下效果v P h F 7 ~ A
可以看到渲染引擎为文字部分单独创建了一个图层。
在布局树中的节点如果拥有对应的图层,这个节点就是一个图层,如果没有,这个4 M i u节点就属于父节点的图层,如下图:
图层绘制
创建好图层树后,渲染引擎会绘制图层树中的每个图层。渲染引擎会将图层绘制分解为很多小的绘制指令,然后将这些指令按照顺序组成待绘制列表,我们可以打开控制台的 layee o - 2 S 5 v o Brs ,选择 document 层,看一下效果
栅格化操作
栅格化就是将图块转h $ v Q d _换位位图,图块是栅格化执^ { # r o 7 ) 1 6行的最小单位。渲染进程维护了一个栅格化的线程池,所有图块的栅格化都是在线程池内执行的。d F { ] % g # C ^
图层绘制列表准备好之后,主线程会把这个绘制列表提交给合成线程,; 5 : w f $绘制操作由渲染引擎中的合成线程来完成。
合成线程将图层划分为图块,然后合成线程会按照视口(可见区域)附近/ i 1的图块优先生成位图。
合成与显示
所有的图块都被光栅化后,合成线程会生成一个绘制图块的命令( DrawQuad ),然后将该命令提交给浏览器进程。浏览器进程里面 viz 组件用来接收 DrawQuad 命令,将其页面内容绘制到内存@ : = c x中,最后将内存显示到屏幕。这个时候,我们就看到了页面
完善渲染流水线示意图
根据上文中描述,我们可以画出这样一张图
我还在网上找到了另外一张图
这两张图都是描述浏览器的渲染流程的。