页面渲染机制

简单渲染过程

url解析:html

  1. 用户输入URL地址web

  2. 浏览器解析URL解析出主机名后端

  3. 浏览器将主机名转换成服务器ip地址(浏览器先查找本地DNS缓存列表 没有的话 再向浏览器默认的DNS服务器发送查询请求 同时缓存)浏览器

  4. 浏览器将端口号从URL中解析出来缓存

  5. 浏览器创建一条与目标Web服务器的TCP链接(三次握手)bash

  6. 浏览器向服务器发送一条HTTP请求报文服务器

  7. 服务器向浏览器返回一条HTTP响应报文app

  8. 关闭链接 浏览器解析文档框架

  9. 若是文档中有资源 重复6 7 8 动做 直至资源所有加载完毕dom

html解析:

  1. 将HTML构建成一个DOM树(DOM = Document Object Model 文档对象模型),DOM 树的构建过程是一个深度遍历过程:当前节点的全部子节点都构建好后才会去构建当前节点的下一个兄弟节点。
  2. 将CSS解析成CSS去构造CSS Rule Tree
  3. 根据DOM树和CSSOM来构造 Rendering Tree(渲染树)。注意:Rendering Tree 渲染树并不等同于 DOM 树,由于一些像 Header 或 display:none 的东西就不必放在渲染树中了。
  4. 有了Render Tree,浏览器已经能知道网页中有哪些节点、各个节点的CSS定义以及他们的从属关系。
  5. 下一步操做称之为Layout,顾名思义就是计算出每一个节点在屏幕中的位置 layout render tree。
  6. 再下一步就是绘制(Paint),即遍历render树,并使用浏览器UI后端层绘制每一个节点。

Render Tree

渲染树,表明一个文档的视觉展现,浏览器经过它将文档内容绘制在浏览器窗口,展现给用户,它由按顺序展现在屏幕上的一系列矩形对象组成,这些矩形对象都带有字体,颜色和尺寸,位置等视觉样式属性。对于这些矩对象,FireFox称之为框架(frame),Webkit浏览器称之为渲染对象(render object, renderer),后文统称为渲染对象。

每个渲染对象都表明着其对应DOM节点的CSS盒子,该盒子包含了尺寸,位置等几何信息,同时它指向一个样式对象包含其余视觉样式信息。

每个渲染对象都对应着DOM节点,可是非视觉(隐藏,不占位)DOM元素不会插入渲染树,如<head>元素或声明display: none;的元素,渲染对象与DOM节点不是简单的一对一的关系,一个DOM能够对应一个渲染对象,但一个DOM元素也可能对应多个渲染对象,由于有不少元素不止包含一个CSS盒子,如当文本被折行时,会产生多个行盒,这些行会生成多个渲染对象;又如行内元素同时包含块元素和行内元素,则会建立一个匿名块级盒包含内部行内元素,此时一个DOM对应多个矩形对象(渲染对象)。

Layout

建立渲染树后,下一步就是布局(Layout),或者叫回流(reflow,relayout),这个过程就是经过渲染树中渲染对象的信息,计算出每个渲染对象的位置和尺寸,将其安置在浏览器窗口的正确位置,而有些时候咱们会在文档布局完成后对DOM进行修改,这时候可能须要从新进行布局,也可称其为回流,本质上仍是一个布局的过程,每个渲染对象都有一个布局或者回流方法,实现其布局或回流。

Paint

最后是绘制(paint)阶段或重绘(repaint)阶段,浏览器UI组件将遍历渲染树并调用渲染对象的绘制(paint)方法,将内容展示在屏幕上,也有可能在以后对DOM进行修改,须要从新绘制渲染对象,也就是重绘,绘制和重绘的关系能够参考布局和回流的关系。

Reflow (回流/重排)

当它发现了某个部分发生了变化影响了布局,渲染树须要从新计算。

缘由:

  1. DOM操做,如增长,删除,修改或移动;
  2. 变动内容;
  3. 激活伪类;
  4. 访问或改变某些CSS属性(包括修改样式表或元素类名或使用JavaScript操做等方式);
  5. 浏览器窗口变化(滚动或尺寸变化)

如何减小reflow

  1. 尽量限制reflow的影响范围。须要改变元素的样式,不要经过父级元素影响子元素。最好直接加在子元素上。
  2. 经过设置style属性改变结点样式的话,每设置一次都会致使一次reflow。因此最好经过设置class的方式。
  3. 减小没必要要的DOM层级(DOM depth)。改变DOM树中的一级会致使全部层级的改变,上至根部,下至被改变节点的子节点。这致使大量时间耗费在执行reflow上面。
  4. 避免没必要要的复杂的CSS选择器,尤为是后代选择器(descendant selectors),由于为了匹配选择器将耗费更多的CPU。

Repaint(重绘)

改变了某个元素的背景颜色,文字颜色等,不影响元素周围或内部布局的属性,将只会引发浏览器的repaint,根据元素的新属性从新绘制,使元素呈现新的外观。重绘不会带来从新布局,并不必定伴随重排; Reflow要比Repaint更花费时间,也就更影响性能。因此在写代码的时候,要尽可能避免过多的Reflow。

script解析

或许是因为一般会在JavaScript脚本中改变文档DOM结构,因而浏览器以同步方式解析,加载和执行脚本,浏览器在解析文档时,当解析到<script>标签时,会解析其中的脚本(对于外链的JavaScript文件,须要先加载该文件内容,再进行解析),而后当即执行,这整个过程都会阻塞文档解析,直到脚本执行完才会继续解析文档。就是说因为脚本是同步加载和执行的,它会阻塞文档解析,这也解释了为何如今一般建议将<script>标签放在</body>标签前面,而不是放在<head>标签里。如今HTML5提供defer和async两个属性支持延迟和异步加载JavaScript文件

defer和async

固然咱们能够经过设置deferasync属性来异步加载不过重要的脚本

这两个属性都告诉浏览器,他可能会在后台加载脚本时继续解析HTML,而后再在加载后执行脚本,这样脚本下载不会阻止DOM构建和页面呈现。用户能够在全部脚本完成加载以前看到页面

二者的区别就是他们将在那一刻执行脚本,在这以前咱们须要了解浏览器为其加载的每一个网页追踪细粒度时间戳

img

  • domLoading: 浏览器即将开始解析第一批收到的HTML文档字节
  • domInteractive: 表示浏览器完成对全部HTML的解析而且DOM构建完成的时间点
  • domContentLoaded: 表示DOM准备就绪而且没有样式表阻止JavaScript执行的时间点
  • domComplete: 全部处理完成,而且网页上的全部资源都已经下载完毕
  • loadEvent: 做为每一个网页加载的最后一步,浏览器会触发onload事件,以便触发额外的应用逻辑

defer的执行将在domInteractive完成以后,domContentLoaded以前开始,他保证脚本将按照他们在HTML中出现的顺序执行,而且不会阻塞解析器

img

async脚本在完成下载以后和窗口load事件以前的某一个时间点执行,这意味着异步脚本可能不按他们在HTML中出现的顺序执行,这意味着他们可能会阻止DOM构建

img

defer

<script src="app1.js" defer></script>
<script src="app2.js" defer></script>
<script src="app3.js" defer></script>
复制代码

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

defer 不会改变 script 中代码的执行顺序,示例代码会按照 一、二、3 的顺序执行。因此,defer 与相比普通 script,有两点区别:载入 JavaScript 文件时不阻塞 HTML 的解析,执行阶段被放到 HTML 标签解析完成以后。

async

<script src="app.js" async></script>
<script src="ad.js" async></script>
<script src="statistics.js" async></script>
复制代码

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

从上一段也能推出,多个 async-script 的执行顺序是不肯定的。值得注意的是,向 document 动态添加 script 标签时,async 属性默认是 true

CSS和JS阻塞规则

  1. CSS 不会阻塞 DOM 的解析,但会阻塞 DOM 渲染。
  2. JS 阻塞 DOM 解析,但浏览器会"偷看"DOM,预先下载相关资源。
  3. 浏览器遇到 <script>且没有defer或async属性的 标签时,会触发页面渲染,于是若是前面CSS资源还没有加载完毕时,浏览器会等待它加载完毕在执行脚本。

渲染优化

  1. HTML文档结构层次尽可能少,最好不深于六层;
  2. 脚本尽可能后放,放在前便可;
  3. 少许首屏样式内联放在标签内;
  4. 样式结构层次尽可能简单;
  5. 在脚本中尽可能减小DOM操做,尽可能缓存访问DOM的样式信息,避免过分触发回流;
  6. 减小经过JavaScript代码修改元素样式,尽可能使用修改class名方式操做样式或动画;
  7. 动画尽可能使用在绝对定位或固定定位的元素上;
  8. 隐藏在屏幕外,或在页面滚动时,尽可能中止动画;
  9. 尽可能缓存DOM查找,查找器尽可能简洁;
  10. 涉及多域名的网站,能够开启域名预解析

参考

  1. https://www.cnblogs.com/CandyManPing/p/6635008.html
  2. http://blog.codingplayboy.com/2017/03/29/webpage_render/
相关文章
相关标签/搜索