必须明白的浏览器渲染机制

前言

浏览器是咱们平常开发的重要的工具,那么你了解浏览器吗?即便在前端面试中,咱们也常常会遇到:在浏览器地址中从输入url地址到出现页面,这个过程发生了什么?介绍一下重绘和回流?这一类关于浏览器的问题。咱们可能会知道大概的轮廓但对于具体的细节倒是不那么清楚,那么今天咱们就从浏览器组成开始来了解一下浏览器的渲染机制javascript

浏览器组成

浏览器主要由7个部分组成:css

  • 用户界面(User Interface):定义了一些经常使用的浏览器组件,好比地址栏,返回、书签等等
  • 数据持久化(Data Persistence):指浏览器的cookie、local storage等组件
  • 浏览器引擎(Browser engine):平台应用的相关接口,在用户界面和呈现引擎之间传送指令。
  • 渲染引擎(Rendering engine):处理HTML、CSS的解析与渲染
  • JavaScript解释器(JavaScript Interpreter):解析和执行JavaScript代码
  • 用户界面后端(UI Backend):指浏览器的的图形库等
  • 网络(Networking):用于网络调用,好比HTTP请求

浏览器内核

浏览器内核分为两部分:渲染引擎(layout engineer或Rendering Engine)和JS引擎html

  • 渲染引擎:负责取得网页的内容(HTML、XML、图像等等)、整理讯息(例如加入CSS等),以及计算网页的显示方式,而后会输出至显示器或打印机
  • JS引擎:负责解析和执行javascript来实现网页的动态效果 浏览器的内核的不一样对于网页的语法解释会有不一样,因此渲染的效果也不相同。全部网页浏览器、电子邮件客户端以及其它须要编辑、显示网络内容的应用程序都须要内核,最开始渲染引擎和JS引擎并无区分的很明确,后来JS引擎愈来愈独立,内核就倾向于只指渲染引擎

常见的浏览器内核:Trident(IE)、Gecko(火狐)、Blink(Chrome、Opera)、Webkit(Safari)前端

页面加载流程

在了解浏览器渲染过程以前,先来了解一下页面的加载流程。有助于更好理解后续渲染过程。从浏览器地址中从输入url地址到渲染出一个页面,会通过如下过程。 1.浏览器输入的url地址通过DNS解析得到对应的IP 2.向服务器发起TCP的3次握手 3.创建连接后,浏览器向该IP地址发送http请求 4.服务器接收到请求,返回一堆 HMTL 格式的字符串代码 5.浏览器得到html代码,解析成DOM树 6.获取CSS并构建CSSOM 7.将DOM与CSSOM结合,建立渲染树 8.找到全部内容都处于网页的哪一个位置,布局渲染树 9.最终绘制出页面java

浏览器渲染机制

咱们将要介绍的浏览器渲染过程主要步骤是5-9步,能够用下面的图来形象的展现 面试

image

解析HTML成DOM树

这个解析过程大概能够分为几个步骤: segmentfault

image
第一步:浏览器从磁盘或网络读取HTML的原始字节,也就是传输的0和1这样的字节数据,并根据文件的指定编码(例如 UTF-8)将它们转换成字符串。 第二步:将字符串转换成Token,例如:“”、“”等。Token中会标识出当前Token是“开始标签”或是“结束标签”亦或是“文本”等信息 第三步:在每一个Token被生成后,会马上消耗这个Token建立出节点对象,所以在构建DOM的过程当中,不是等待全部的Token都生成后才去构建DOM,而是一边生成Token一边消耗来生成节点对象。

注意:带有结束标签标识的Token不会建立节点对象 第四步:经过“开始标签”与“结束标签”来识别并关联节点之间的关系。当全部Token都生成并消耗完毕后,咱们就获得了一颗完整的DOM树。后端

可是如今有一个疑问,节点之间的关联关系是如何维护的呢? 上面咱们提到Token会标识是“开始标签”仍是“结束标签”,如下图为例:“Hello”Token位于“title”开始标签与“title”结束标签之间,代表“Hello”Token是“title”Token的子节点。同理“title”Token是“head”Token的子节点。 浏览器

image

构建CSSOM

既然有了html解析,那css解析也是必不可少的,解析css构建CSSOM 的过程和构建DOM的过程很是的类似。当浏览器接收到一段CSS,浏览器首先要作的是识别出Token,而后构建节点并生成CSSOM 缓存

image
节点中样式能够经过继承获得,也能够本身设置,所以在构建的过程当中浏览器得递归 CSSOM 树,而后肯定具体的元素究竟是什么样式。为了CSSOM的完整性,也只有等构建完毕才能进入到下一个阶段,哪怕DOM已经构建完,它也得等CSSOM,而后才能进入下一个阶段。

CSS匹配HTML元素是一个至关复杂和有性能问题的事情。因此,DOM树要小,CSS尽可能用id和class,千万不要过渡层叠下去 因此,CSS的加载速度与构建CSSOM的速度将直接影响首屏渲染速度,所以在默认状况下CSS被视为阻塞渲染的资源

构建渲染树

当咱们生成DOM树和CSSOM树后,咱们须要将这两颗树合并成渲染树,在构建渲染树的过程当中浏览器须要作以下工做:

  • 从 DOM 树的根节点开始遍历每一个可见节点。
  • 有些节点不可见(例如脚本Token、元Token等),由于它们不会体如今渲染输出中,因此会被忽略。
  • 某些节点被CSS隐藏,所以在渲染树中也会被忽略。例如某些节点设置了display: none属性。
  • 对于每一个可见节点,为其找到适配的 CSSOM 规则并应用它们

image

渲染阻塞

在渲染的过程当中,遇到一个script标记时,就会中止渲染,去请求脚本文件并执行脚本文件,由于浏览器渲染和 JS 执行共用一个线程,并且这里必须是单线程操做,多线程会产生渲染 DOM 冲突。JavaScript的加载、解析与执行会严重阻塞DOM的构建。只有等到脚本文件执行完毕,才会去继续构建DOM。

js不单会阻塞DOM构建,还会致使CSSOM也阻塞DOM的构建,若是JavaScript脚本还操做了CSSOM,而正好这个CSSOM尚未下载和构建,浏览器甚至会延迟脚本执行和构建DOM,直至完成其CSSOM的下载和构建,而后再执行JavaScript,最后在继续构建DOM

所以script的位置很重要,在实际使用过程当中遵循如下两个原则:

  • CSS 优先:引入顺序上,CSS 资源先于 JavaScript 资源。
  • JS置后:咱们一般把JS代码放到页面底部,且JavaScript 应尽可能少影响 DOM 的构建

布局与绘制

浏览器拿到渲染树后,就会从渲染树的根节点开始遍历,而后肯定每一个节点对象在页面上的确切大小与位置,一般这一行为也被称为“自动重排”。布局阶段的输出是一个盒子模型,它会精确地捕获每一个元素在屏幕内的确切位置与大小,全部相对测量值都将转换为屏幕上的绝对像素。这一过程也可称为回流

布局完成后,浏览器会当即发出“Paint Setup”和“Paint”事件,将渲染树转换成屏幕上的像素。

性能优化策略

在咱们了解浏览器的渲染机制后,DOM 和 CSSOM 结构构建顺序,咱们能够针对性能优化问题给出一些方案,提高页面性能。

1.回流(reflow)与重绘(repaint)

当元素的样式发生变化时,浏览器须要触发更新,从新绘制元素。这个过程当中,有两种类型的操做,即重绘与回流。

  • 重绘(repaint): 当元素样式的改变不影响布局时,浏览器将使用重绘对元素进行更新,此时因为只须要UI层面的从新像素绘制,所以损耗较少
  • 回流(reflow): 当元素的尺寸、结构或触发某些属性时,浏览器会从新渲染页面,称为回流。此时,浏览器须要从新通过计算,计算后还须要从新页面布局,所以是较重的操做。会触发回流的操做:
  • 添加或删除可见的DOM元素
  • 元素的位置发生变化
  • 元素的尺寸发生变化(包括外边距、内边框、边框大小、高度和宽度等)
  • 内容发生变化,好比文本变化或图片被另外一个不一样尺寸的图片所替代。
  • 页面一开始渲染的时候(这确定避免不了)
  • 浏览器的窗口尺寸变化(由于回流是根据视口的大小来计算元素的位置和大小的

注意:回流必定会触发重绘,而重绘不必定会回流,重绘的开销较小,回流的代价较高

所以为了减小性能优化,咱们能够尽可能避免回流或者重绘操做 css

  • 避免使用table布局
  • 将动画效果应用到position属性为absolute或fixed的元素上

javascript

  • 避免频繁操做样式,可汇总后统一 一次修改
  • 尽可能使用class进行样式修改
  • 减小dom的增删次数,可以使用 字符串 或者 documentFragment 一次性插入
  • 极限优化时,修改样式可将其display: none后修改
  • 避免屡次触发上面提到的那些会触发回流的方法,能够的话尽可能用 变量存住

async和defer的做用是什么?有什么区别?

defer 和 async 属性的区别:

image
其中蓝色线表明JavaScript加载;红色线表明JavaScript执行;绿色线表明 HTML 解析 1)状况1 <scriptsrc="script.js"> 没有 defer 或 async,浏览器会当即加载并执行指定的脚本,也就是说不等待后续载入的文档元素,读到就加载并执行。

2)状况2 (异步下载) async 属性表示异步执行引入的 JavaScript,与 defer 的区别在于,若是已经加载好,就会开始执行——不管此刻是 HTML 解析阶段仍是 DOMContentLoaded 触发以后。须要注意的是,这种方式加载的 JavaScript 依然会阻塞 load 事件。换句话说,async-script 可能在 DOMContentLoaded 触发以前或以后执行,但必定在 load 触发以前执行。

3)状况3 <scriptdefersrc="script.js">(延迟执行) defer 属性表示延迟执行引入的 JavaScript,即这段 JavaScript 加载时 HTML 并未中止解析,这两个过程是并行的。整个 document 解析完毕且 defer-script 也加载完成以后(这两件事情的顺序无关),会执行全部由 defer-script 加载的 JavaScript 代码,而后触发 DOMContentLoaded 事件。

defer 与相比普通 script,有两点区别:

  • 载入 JavaScript 文件时不阻塞 HTML 的解析,执行阶段被放到 HTML 标签解析完成以后;
  • 在加载多个JS脚本的时候,async是无顺序的加载,而defer是有顺序的加载

js优化能够在script标签加上 defer属性 和 async属性用于在不阻塞页面文档解析的前提下,控制脚本的下载和执行

其余: CSS 标签的 rel属性 中的属性值设置为 preload 可以让你在你的HTML页面中能够指明哪些资源是在页面加载完成后即刻须要的,最优的配置加载顺序,提升渲染性能

首屏优化加载

  • 减小首屏CGI的计算量:好比在微信8.8无现金日H5开发中,前端但愿拿到用户的我的信息、消费记录、排名三类数据,若是只经过一个CGI来处理,那么后台响应时间确定会变长;因为在H5的首屏中,只包含了用户信息,消费记录、排名都在第2屏和第3屏,此时其实能够利用异步的方式来拿消费记录、排名的数据。
  • 页面瘦身:压缩HTML、CSS、JavaScript。
  • 减小请求:CSS、JavaScript文件数尽可能少,甚至当CSS、JS的代码很少时,能够考虑直接将代码内嵌到页面中。
  • 多用缓存:缓存能大幅度下降页面非首次加载的时间。
  • 少用table布局,浏览器在渲染table时会消耗较多资源,并且只有table里有一点变化,整个table都会从新渲染。
  • 作预加载:部分H5页面首屏可能要下载较多的静态资源,好比图片,这时为了不加载时出现“难看”的页面,用预加载(loading的方式)作一个过渡

总结

咱们已经将浏览器的渲染机制了解了一遍,不只了解到一些性能优化方案,也能够得出结论: 浏览器渲染的关键路径共分五个步骤:

构建DOM -> 构建CSSOM -> 构建渲染树 -> 布局 -> 绘制

参考连接

相关文章
相关标签/搜索