浏览器渲染机制

概述

浏览器渲染依靠其渲染引擎(注意和 js 引擎的区别,这是解析运行 js 代码的,比如谷歌的 V8 引擎),渲染引擎通过解析 HTML 和 CSS 构建渲染树,然后调用 GPU 接口在屏幕上呈现出图像。
我们必须知道,电脑显示器呈现图像,其实都是显卡在发生作用。针对于显卡的编程,那是属于比较底层的代码了,说不定还会扯到操作系统,显卡编程提供的接口也会特别多、杂。
如果我们不是做和渲染引擎开发的工作,比如3d游戏引擎、操作系统界面开发等等,完全没必要了解这些,毕竟这些都属于造轮子的工作,而且一个人也难以完成,这是团队的工作。
我们前端知道的也就是css、html、js 这些,正是由于浏览器渲染引擎的存在,我们的代码的 UI 展示才会展示在用户的面前。
如同java开发,浏览器渲染引擎就是 HTML、CSS 的 jvm (java 虚拟机)。所以,我们不需要知道显卡底层,但是对于渲染引擎了解一下还是必要的。

一、常见浏览器渲染引擎一览

按照时间排序展示:

  • 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,生成新的 Render Tree ,这种情况叫做强制同步布局,这是一个时间花费长的过程。在后文的性能章节,我会对此更多阐述。

三、渲染性能

在后面的阐述中,我会以 Webkit 渲染引擎为例子,后续的相关术语以及概念也是以 Webkit 为参考,其他引擎都大差不差。

3.1 Layout 相关

3.1.1 避免 js 连续触发 Layout

了解 js 的人应该都知道,js 是单线程,虽然现在有了 web worker 可以开辟子线程(不能操纵渲染引擎),但是只有 js 主线程能操控渲染引擎,这句话的意思就是 js 引擎 和渲染引擎共用一个主要的线程,它们是互斥的!
js 涉及到 Layout 的操作时,会触发 Layout,此时 js 任务挂起,Layout 完毕再继续 js 操作。如果大量的这种 操作连续出现,会造成页面卡顿(因为 Layout 很费时,导致 Render Tree 得不到及时的更新)。

解决方式是采用类似 CPU 时间分片的方式,将一系列的操作分解成一个个合适的操作(合适的意思是说,该操作的花费总合小于一个固定的时间),在某段时间只执行一个,通用的是采用 setTimeout 实现。 不过 setTimeout 有一个弊端,了解 js 的知道,js 是单线程,js 运行时间超过 setTimeout 的时间,则 setTimeout 完成后得不到及时的处理,这样导致 Render Tree 照样得不到及时更新,照样卡顿。

说到这,我们得了解 GUI 在浏览器的工作方式,通常 GUI 在 1 秒内的绘制次数和屏幕刷新率一致,通常是60帧。有个浏览器的新接口 window.requestAnimationFrame 解决了 setTimeout 的弊端,这个接口会在下一帧绘制前调用,确保了时间的一致性。

3.1.2 避免操作大量DOM

Layout 的速度取决于处理 DOM 的多寡,因为一个 DOM 会产生蝴蝶效应,会影响很多 DOM 的布局,而布局调整是同步进行的,js 引擎会被挂起。
解决方式:

  • 尽可能减少会涉及很多 DOM 的操作,举个例子:比如可以 js 生成未挂载在 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 使用的同一个主线程,这意味着它们是同步的,一方执行,另一方就得停下。关于这个主线程的描述,在 MDN谷歌开发者web文档 都有详尽的说明,确实是共用同一个线程的,这里我不多加阐述。

在我个人的测试中,不同渲染引擎的执行和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产生的修效果直接合并,再尔进行渲染。

4.2 相关链接

How Browsers Work: Behind the scenes of modern web browsers

posted @ 2021-07-06 14:16  Sebastian·S·Pan  阅读(614)  评论(0编辑  收藏  举报