浏览器渲染机制
概述
浏览器渲染依靠其渲染引擎,渲染引擎通过解析 HTML 和 CSS 构建渲染树,然后调⽤ GPU 接⼝在屏幕上呈现出图像。
我们必须知道,电脑显⽰器呈现图像,其实都是显卡在发⽣作⽤。针对于显卡的编程,那是属于⽐较底层的代码了,说不定还会扯到操作系统,估计显卡编程提供的接⼝也会特别多、杂。
如果我们不是做和渲染引擎开发的⼯作,⽐如3d游戏引擎、操作系统界⾯开发等等,完全没必要了解这些,毕竟这些都属于造轮⼦的⼯作,⽽且⼀个⼈也难以完成,这是团队的⼯作。
我们前端知道的也就是css、html、js这些,正是由于浏览器渲染引擎的存在,我们的代码的 UI 展⽰才会展⽰在⽤户的⾯前。
如同java开发,浏览器渲染引擎就是 HTML、CSS 的 jvm (java 虚拟机)。所以,我们不需要知道显卡底层,但是对于渲染引擎了解⼀下还是必要的。
⼀、常见浏览器渲染引擎⼀览
按照时间排序展⽰:
css和html和js怎么结合Gecko:前⾝来⾃伊利诺伊⼤学的NCSA,由Netscape(⽹景)开源,Netscape浏览器、Mozilla Firefox 浏览器等使⽤
Trident(也称MSHTML):微软公司创建,IE浏览器
KHTML:由开源志愿者开发的⼀个开源引擎,Linux 平台开始使⽤,Konqueror 浏览器使⽤
Webkit:基于 KHTML 引擎,Apple 公司创建,Safari 浏览器以及其他其他⼀些 Mac OS X 程序在使⽤,Chrome 浏览器最初也在使⽤Blink:基于 Webkit 引擎,⾕歌公司创建,Chrome 浏览器使⽤
Chakra(也称 EdgeHTML ):微软公司创建,Edge 浏览器和其他 UWP 浏览器使⽤
⼆、常见渲染引擎⼯作原理:
2.1 常见渲染引擎逻辑图
如图所⽰:
图2.1.1 WebKit引擎
图2.1.2 Gecko引擎
对于这两张图,我会对以下的⼏个⽅⾯略过:
解析 CSS、 HTML 的不讲,这部分是将代码编译成特定的格式,属于编译器的知识;
如何合并 CSS、HTML 构建 Render(Frame) Tree 也不讲,这也是个⼤⼯程;
还有最后的Display 。Display 就是展⽰,指将 Render(Frame) Tree 转换成像素的过程,也就是屏幕展现出的效果,我们眼睛能看见的东西。这涉及到 GPU 层⾯的知识,略过。
这⾥只讲图的2个部分,分别是:
Render Tree,事实上,Frame Tree 和这个概念没什么不同,只不过这是⽕狐的说法
Layout,Reflow 同上,也是和Layout相同的概念
2.2 Render Tree(Frame Tree)
中⽂解释是渲染树,它描述了各个 DOM 元素的层级、位置关系(注意此处,描述的是位置结构的东西!),是其逻辑实现。
你要是了解 Vue 或者 React 的虚拟 DOM,就应该懂 Render Tree 的意思,它也是这么⼀个东西,不过它是真实 DOM ⾯向 GUI 的映射(⽤来帮助渲染的),⽽虚拟 DOM 是组件⾯向真实 DOM 的映射。
2.3 Layout(Reflow)
在⾕歌、IE、Safari等等浏览器的叫法叫做 Layout(布局),⽕狐叫做 Reflow(回流),这是⼀种⾏为,⼀种构建和调整 Render Tree 的⾏为。你可以想象成在 Vue 和 React 中,对虚拟 DOM 的diff 以及调整。说到这,我看了很多⼈的说法都是采⽤Webkit的逻辑图,说着回流的概念,其实应该说布局的。
JS 操作⼀个DOM,如果涉及到 Layout(Reflow),⽐如元素删除、调整宽度、调整⾏⾼等等会影响布局的操作(具体有哪些),会马上终⽌ JS,触发 Layout,这种情况叫做强制同步布局,这是⼀个时间花费长的过程。在后⽂的性能章节,我会对此更多阐述。
三、渲染性能
在后⾯的阐述中,我会以 Webkit 渲染引擎为例⼦,后续的相关术语以及概念也是以 Webkit 为参考,其他引擎都⼤差不差。
3.1 Layout 相关
3.1.1 避免 JS 连续触发 Layout
JS 涉及到 Layout 的操作时,会触发 Layout,此时 JS 任务挂起,Layout 完毕再继续 JS 操作。如果⼤量的这种操作连续出现,会造成页⾯卡顿(因为 Layout 很费时,导致 Render Tree 得不到及时的更新)。
解决⽅式是采⽤类似CPU时间分⽚的⽅式,将⼀系列的操作分解成⼀个个合适的操作(合适的意思是说,该操作的花费总合⼩于⼀个固定的时间),在某段时间只执⾏⼀个,通⽤的是采⽤ setTimeout 实现。不过 setTimeout 有⼀个弊端,了解 JS 的知道,JS 是单线程,JS 运⾏时间超过 setTimeout 的时间,则 setTimeout 完成后得不到及时的处理,这样导致 Render Tree 照样得不到及时更新,照样卡顿。
说到这,我们得了解 GUI 在浏览器的⼯作⽅式,通常 GUI 在 1 秒内的绘制次数和屏幕刷新率⼀致,通常是60帧。有个浏览器的新接⼝解决了 setTimeout 的弊端,这个接⼝会在每⼀帧绘制前调⽤,确保了时间的⼀致性。
3.1.2 避免操作⼤量DOM
Layout 的速度取决于处理 DOM 的多寡,因为⼀个 DOM 会产⽣蝴蝶效应,会影响很多 DOM 的布局。
解决⽅式:
尽可能减少会涉及很多 DOM 的操作
多使⽤ transition、opacity 这些 CSS 属性,这些属性在某些浏览器中有相关的 GUI 加速优化(⽐如⾕歌,这些属性是⾛ GPU ⽽不是CPU,这样 CPU 可以处理 js 代码)
多层次的布局(⽐如脱离了⽂档流的float,absolute,全局的z-index等等),会在其内容范围内执⾏ Layout,所以有选择的布局也是优化之⼀
3.2 编译器相关
减少HTML、CSS⽂档不必要的换⾏,也就是说压缩后的 HTML、CSS 执⾏的速度快,这是在词法解析的层⾯加速
3.3 ⽂件加载优化
在⾕歌浏览器中,如果你的 css 代码在 body 后,则解析成 DOM Tree 后便会开始初步渲染,直接显⽰在屏幕上。这意味着,CSS 加载完成后将会再次渲染,这是对引擎的浪费,所以通常的优化⼿段是CSS放在body前引⼊。
还有就是 JS 和渲染引擎在同⼀个主线程上运⾏,所以它们是互斥的。设置 JS ⽂件加载为异步或者在最后加载,尽可能让 JS 代码在 HTML 元素 Body 之后执⾏,不然页⾯渲染会卡顿。
四、其他
4.1 各个渲染引擎的异同
相同的是,我们知道渲染引擎、JS 使⽤的同⼀个主线程,⼀⽅执⾏,另⼀⽅就得停下,关于这个主线程的描述,在和都有详尽的说明,确实是同⼀个线程且互斥的,这⾥我不多加阐述。
在我个⼈的测试中,不同渲染引擎的执⾏和JS的执⾏有点点不同:
在 WebKit 引擎中,构建 Render Tree 后不会⽴即渲染,⽽是执⾏ js 代码
⽽⽕狐浏览器则是会先渲染,再执⾏之后的js代码
下⾯的代码是我的例⼦,⼤家可以尝试:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<style>
p {
color: red;
}
</style>
<body>
<p>1</p>
</body>
<script>
alert("中断渲染线程!") //交互事件会阻碍主线程的执⾏,js和渲染都会停下⼯作
</script>
</html>
我在 chrome 浏览器和 safari 浏览器中的测得的效果⼀致:有弹窗,但是屏幕上什么都没出现
在⽕狐浏览器中测试的效果:有弹窗,屏幕上是理想的效果,出现了红⾊的数字 1 。
个⼈⽐较赞同⽕狐的做法,先渲染再执⾏js代码,这是⽐较⼈性化的,⽤户希望看见的是效果⽽不是关⼼js代码。
猜测 WebKit 引擎可能是为了性能考量,毕竟渲染费时费⼒,先运⾏js代码,将第⼀遍运⾏js产⽣的修效果直接合并,再尔进⾏渲染。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论