本网站(662p.com)打包出售,且带程序代码数据,662p.com域名,程序内核采用TP框架开发,需要联系扣扣:2360248666 /wx:lianweikj
精品域名一口价出售:1y1m.com(350元) ,6b7b.com(400元) , 5k5j.com(380元) , yayj.com(1800元), jiongzhun.com(1000元) , niuzen.com(2800元) , zennei.com(5000元)
需要联系扣扣:2360248666 /wx:lianweikj
你不知道的浏览器渲染原理
追忆似水年华 · 1372浏览 · 发布于2019-05-09 +关注

前言

浏览器的内核是指支持浏览器运行的最核心的程序,分为两个部分的,一是渲染引擎,另一个是 JS 引擎。渲染引擎在不同的浏览器中也不是都相同的。目前市面上常见的浏览器内核可以分为这四种:Trident(IE)、Gecko(火狐)、Blink(Chrome、Opera)、Webkit(Safari)。这里面大家最耳熟能详的可能就是 Webkit 内核了,Webkit 内核是当下浏览器世界真正的霸主。

本文我们就以 Webkit 为例,对现代浏览器的渲染过程进行一个深度的剖析。

页面加载过程

在介绍浏览器渲染过程之前,我们简明扼要介绍下页面的加载过程,有助于更好理解后续渲染过程。

要点如下:

  • 浏览器根据 DNS 服务器得到域名的 IP 地址;

  • 向这个 IP 的机器发送 HTTP 请求;

  • 服务器收到、处理并返回 HTTP 请求;

  • 浏览器得到返回内容。

例如在浏览器输入https://code.662p.com,然后经过 DNS 解析,code.662p.com对应的 IP 是36.248.217.149(不同时间、地点对应的 IP 可能会不同)。然后浏览器向该 IP 发送 HTTP 请求。

服务端接收到 HTTP 请求,然后经过计算(向不同的用户推送不同的内容),返回 HTTP 请求,返回的内容如下:

image

其实就是一堆 HMTL 格式的字符串,因为只有 HTML 格式浏览器才能正确解析,这是 W3C 标准的要求。接下来就是浏览器的渲染过程。

浏览器渲染过程

浏览器渲染过程大体分为如下三部分:

1)浏览器会解析三个东西:

一是 HTML/SVG/XHTML,HTML 字符串描述了一个页面的结构,浏览器会把 HTML 结构字符串解析转换 DOM 树形结构。

二是 CSS,解析 CSS 会产生 CSS 规则树,它和 DOM 结构比较像。

三是 Javascript 脚本,等到 Javascript 脚本文件加载后, 通过 DOM API 和 CSSOM API 来操作 DOM Tree 和 CSS Rule Tree。

2)解析完成后,浏览器引擎会通过 DOM Tree 和 CSS Rule Tree 来构造 Rendering Tree。

  • Rendering Tree 渲染树并不等同于 DOM 树,渲染树只会包括需要显示的节点和这些节点的样式信息。

  • CSS 的 Rule Tree 主要是为了完成匹配并把 CSS Rule 附加上 Rendering Tree 上的每个 Element(也就是每个 Frame)。

  • 然后,计算每个 Frame 的位置,这又叫 layout 和 reflow 过程。

3)最后通过调用操作系统 Native GUI 的 API 绘制。

接下来我们针对这其中所经历的重要步骤详细阐述

构建 DOM

浏览器会遵守一套步骤将 HTML 文件转换为 DOM 树。宏观上,可以分为几个步骤:

构建 DOM 的具体步骤

浏览器从磁盘或网络读取 HTML 的原始字节,并根据文件的指定编码(例如 UTF-8)将它们转换成字符串。

在网络中传输的内容其实都是 0 和 1 这些字节数据。当浏览器接收到这些字节数据以后,它会将这些字节数据转换为字符串,也就是我们写的代码。

将字符串转换成 Token,例如:等。Token 中会标识出当前 Token 是“开始标签”或是“结束标签”亦或是“文本”等信息

这时候你一定会有疑问,节点与节点之间的关系如何维护?

事实上,这就是 Token 要标识“起始标签”和“结束标签”等标识的作用。例如“title”Token 的起始标签和结束标签之间的节点肯定是属于“head”的子节点。

上图给出了节点之间的关系,例如:“Hello”Token 位于“title”开始标签与“title”结束标签之间,表明“Hello”Token 是“title”Token 的子节点。同理“title”Token 是“head”Token 的子节点。

  • 生成节点对象并构建 DOM

事实上,构建 DOM 的过程中,不是等所有 Token 都转换完成后再去生成节点对象,而是一边生成 Token 一边消耗 Token 来生成节点对象。换句话说,每个 Token 被生成后,会立刻消耗这个 Token 创建出节点对象。注意:带有结束标签标识的 Token 不会创建节点对象。

接下来我们举个例子,假设有段 HTML 文本:

    Web page parsing            Web page parsing        This is an example Web page.

上面这段 HTML 会解析成这样:

构建 CSSOM

DOM 会捕获页面的内容,但浏览器还需要知道页面如何展示,所以需要构建 CSSOM。

构建 CSSOM 的过程与构建 DOM 的过程非常相似,当浏览器接收到一段 CSS,浏览器首先要做的是识别出 Token,然后构建节点并生成 CSSOM。

在这一过程中,浏览器会确定下每一个节点的样式到底是什么,并且这一过程其实是很消耗资源的。因为样式你可以自行设置给某个节点,也可以通过继承获得。在这一过程中,浏览器得递归 CSSOM 树,然后确定具体的元素到底是什么样式。

注意:CSS 匹配 HTML 元素是一个相当复杂和有性能问题的事情。所以,DOM 树要小,CSS 尽量用 id 和 class,千万不要过渡层叠下去

构建渲染树

当我们生成 DOM 树和 CSSOM 树以后,就需要将这两棵树组合为渲染树。

在这一过程中,不是简单的将两者合并就行了。渲染树只会包括需要显示的节点和这些节点的样式信息,如果某个节点是 display: none 的,那么就不会在渲染树中显示。

我们或许有个疑惑:浏览器如果渲染过程中遇到 JS 文件怎么处理

渲染过程中,如果遇到

 

 

没有 defer 或 async,浏览器会立即加载并执行指定的脚本,也就是说不等待后续载入的文档元素,读到就加载并执行。

2)情况 2

 

 (异步下载)

 

async 属性表示异步执行引入的 JavaScript,与 defer 的区别在于,如果已经加载好,就会开始执行——无论此刻是 HTML 解析阶段还是 DOMContentLoaded 触发之后。需要注意的是,这种方式加载的 JavaScript 依然会阻塞 load 事件。换句话说,async-script 可能在 DOMContentLoaded 触发之前或之后执行,但一定在 load 触发之前执行。

3)情况 3 

(延迟执行)

defer 属性表示延迟执行引入的 JavaScript,即这段 JavaScript 加载时 HTML 并未停止解析,这两个过程是并行的。整个 document 解析完毕且 defer-script 也加载完成之后(这两件事情的顺序无关),会执行所有由 defer-script 加载的 JavaScript 代码,然后触发 DOMContentLoaded 事件。

defer 与相比普通 script,有两点区别:载入 JavaScript 文件时不阻塞 HTML 的解析,执行阶段被放到 HTML 标签解析完成之后。 在加载多个 JS 脚本的时候,async 是无顺序的加载,而 defer 是有顺序的加载。

2. 为什么操作 DOM 慢?

把 DOM 和 JavaScript 各自想象成一个岛屿,它们之间用收费桥梁连接。——《高性能 JavaScript》

JS 是很快的,在 JS 中修改 DOM 对象也是很快的。在 JS 的世界里,一切是简单的、迅速的。但 DOM 操作并非 JS 一个人的独舞,而是两个模块之间的协作。

因为 DOM 是属于渲染引擎中的东西,而 JS 又是 JS 引擎中的东西。当我们用 JS 去操作 DOM 时,本质上是 JS 引擎和渲染引擎之间进行了“跨界交流”。这个“跨界交流”的实现并不简单,它依赖了桥接接口作为“桥梁”(如下图)。

过“桥”要收费——这个开销本身就是不可忽略的。我们每操作一次 DOM(不管是为了修改还是仅仅为了访问其值),都要过一次“桥”。过“桥”的次数一多,就会产生比较明显的性能问题。因此“减少 DOM 操作”的建议,并非空穴来风。

为了帮助大家让学习变得轻松、高效,给大家免费分享一大批资料,帮助大家在成为全栈工程师,乃至架构师的路上披荆斩棘。在这里给大家推荐一个前端全栈学习交流圈:866109386

3. 你真的了解回流和重绘吗?

渲染的流程基本上是这样(如下图黄色的四个步骤):

1. 计算 CSS 样式

2. 构建 Render Tree

3.Layout – 定位坐标和大小

4. 正式开画

注意:上图流程中有很多连接线,这表示了 Javascript 动态修改了 DOM 属性或是 CSS 属性会导致重新 Layout,但有些改变不会重新 Layout,就是上图中那些指到天上的箭头,比如修改后的 CSS rule 没有被匹配到元素。

这里重要要说两个概念,一个是 Reflow,另一个是 Repaint

重绘:当我们对 DOM 的修改导致了样式的变化、却并未影响其几何属性(比如修改了颜色或背景色)时,浏览器不需重新计算元素的几何属性、直接为该元素绘制新的样式(跳过了上图所示的回流环节)。

回流:当我们对 DOM 的修改引发了 DOM 几何尺寸的变化(比如修改元素的宽、高或隐藏元素等)时,浏览器需要重新计算元素的几何属性(其他元素的几何属性和位置也会因此受到影响),然后再将计算的结果绘制出来,这个过程就是回流(也叫重排)。

我们知道,当网页生成的时候,至少会渲染一次。在用户访问的过程中,还会不断重新渲染。重新渲染会重复回流 + 重绘或者只有重绘。

回流必定会发生重绘,重绘不一定会引发回流。重绘和回流会在我们设置节点样式时频繁出现,同时也会很大程度上影响性能。回流所需的成本比重绘高的多,改变父节点里的子节点很可能会导致父节点的一系列回流。

1)常见引起回流属性和方法

任何会改变元素几何信息 (元素的位置和尺寸大小) 的操作,都会触发回流,

  • 添加或者删除可见的 DOM 元素;

  • 元素尺寸改变——边距、填充、边框、宽度和高度;

  • 内容变化,比如用户在 input 框中输入文字;

  • 浏览器窗口尺寸改变——resize 事件发生时;

  • 计算 offsetWidth 和 offsetHeight 属性;

  • 设置 style 属性的值。

2)常见引起重绘属性和方法

3)如何减少回流、重绘

  • 使用 transform 替代 top;

  • 使用 visibility 替换 display: none ,因为前者只会引起重绘,后者会引发回流(改变了布局);

  • 不要把节点的属性值放在一个循环里当成循环里的变量。

<pre style="background: rgb(246, 246, 246); margin: 16px 0px 14px; padding: 14px 15px 12px; border-radius: 3px; border: currentColor; border-image: none; color: rgb(61, 70, 77); text-transform: none; line-height: 1.6; text-indent: 0px; letter-spacing: normal; overflow: auto; font-family: Menlo, Monaco,

相关推荐

RN开发环境的npm私库本地debug调试

manongba · 698浏览 · 2019-05-09 17:03:46
基于iview的router常用控制方式

追忆似水年华 · 999浏览 · 2019-06-03 10:39:21
编程小知识之 JavaScript 文件读取

manongba · 717浏览 · 2019-06-10 09:16:16
10个省时间的 PyCharm 技巧 赶快收藏!

· 702浏览 · 2019-06-10 09:32:01
面试常问到的QT创建对话框三方法

manongba · 1629浏览 · 2019-06-13 09:48:17
加载中

0评论

评论
分类专栏
小鸟云服务器
扫码进入手机网页