浏览器渲染网页的过程

浏览器的主要功能是将用户选择的 web 资源呈现出来,它须要从服务器请求资源,并将其显示在浏览器窗口中,资源的格式一般是 HTML,也包括 PDF、image 及其余格式。css

浏览器的线程

浏览器是多线程的,它们在内核制控下相互配合以保持同步。一个浏览器至少实现三个常驻线程:JavaScript 引擎线程,GUI 渲染线程,浏览器事件触发线程。html

  • GUI 渲染线程:负责渲染浏览器界面 HTML 元素,当界面须要重绘(Repaint)或因为某种操做引起回流(reflow)时,该线程就会执行。在 Javascript 引擎运行脚本期间,GUI 渲染线程都是处于挂起状态的,也就是说被”冻结”了。web

  • JavaScript 引擎线程:主要负责处理 Javascript 脚本程序ajax

  • 定时器触发线程:浏览器定时计数器并非由 JavaScript 引擎计数的, JavaScript 引擎是单线程的, 若是处于阻塞线程状态就会影响记计时的准确, 所以浏览器经过单独线程来计时并触发定时。浏览器

  • 事件触发线程:当一个事件被触发时该线程会把事件添加到待处理队列的队尾,等待 JS 引擎的处理。这些事件包括当前执行的代码块如定时任务、浏览器内核的其余线程如鼠标点击、AJAX 异步请求等。因为 JS 的单线程关系全部这些事件都得排队等待 JS 引擎处理。定时块任何和 ajax 请求等这些异步任务,事件触发线程只是在到达定时时间或者是 ajax 请求成功后,把回调函数放到事件队列当中。缓存

  • 异步 HTTP 请求线程:在 XMLHttpRequest 在链接后是经过浏览器新开一个线程请求, 将检测到状态变动时,若是设置有回调函数,异步线程就产生状态变动事件放到 JavaScript 引擎的处理队列中等待处理。在发起了一个异步请求时,http 请求线程则负责去请求服务器,有了响应之后,事件触发线程再把回到函数放到事件队列当中。性能优化

浏览器渲染流程

1.解析 html,构建 dom 树;解析 CSS 会产生 CSS 规则树

浏览器解析 HTML 文档的源码,而后构造出一个 DOM 树,DOM 树的构建过程是一个深度遍历的过程,当前节点的全部子节点都构建好之后才会去构建当前节点的下一个兄弟节点。服务器

浏览器对 CSS 文件内容进行解析,通常来讲,浏览器会先查找内联样式,而后是 CSS 文件中定义的样式,最后再是浏览器默认的样式,构建 CSS Rule Tree。多线程

2.构建(construct) render 树

根据 DOM 树和 CSSOM 树来构造 Rendering Tree。注意:Rendering Tree 渲染树并不等同于 DOM 树,由于一些像 Header 或 display:none 的东西就不必放在渲染树中了。dom

3.布局(layout) render 树

有了 Render Tree,浏览器已经能知道网页中有哪些节点、各个节点的 CSS 定义以及他们的从属关系,从而去计算出每一个节点在屏幕中的位置。

4.绘制(painting) render 树

按照算出来的规则,经过显卡,把内容画到屏幕上。

5.回流(reflow)

当浏览器发现某个部分发生了点变化影响了布局,须要倒回去从新渲染,内行称这个回退的过程叫 reflow。reflow 会从 这个 root frame 开始递归往下,依次计算全部的结点几何尺寸和位置。

6.重绘(repaint)

改变某个元素的背景色、文字颜色、边框颜色等等不影响它周围或内部布局的属性时,屏幕的一部分要重画,可是元素的几何尺寸没有变。

浏览器对 CSS 和 JS 的解析规则

CSS:

  • CSS 放在 head 中会阻塞页面的渲染(页面的渲染会等到 css 加载完成)
  • CSS 阻塞 JS 的执行 (由于 GUI 线程和 JS 线程是互斥的,由于有可能 JS 会操做 CSS)
  • CSS 不阻塞外部脚本的加载(不阻塞 JS 的加载,但阻塞 JS 的执行,由于浏览器都会有预先扫描器)

JS:

  • 直接引入的 JS 会阻塞页面的渲染(GUI 线程和 JS 线程互斥)
  • 异步加载的 JS (script 标签中添加 defer 属性)不阻塞页面的解析
  • 异步加载的 JS (script 标签中添加 async 属性)下载过程不阻塞页面的解析,下载完成后当即执行,执行过程会阻塞页面的解析
  • JS 不阻塞资源的加载
  • JS 顺序执行,阻塞后续 JS 逻辑的执行

HTML 页面加载和解析流程

  1. 用户输入网址(假设是个 html 页面,而且是第一次访问),浏览器向服务器发出请求,服务器返回 html 文件;
  2. 浏览器开始载入 html 代码,发现< head >标签内有一个< link >标签引用外部 CSS 文件;
  3. 浏览器发出 CSS 文件的请求,服务器返回这个 CSS 文件;(同时 GUI 渲染线程继续执行,互不影响)
  4. 浏览器继续载入 html 中< body >部分的代码,而且 CSS 文件已经拿到手了,开始渲染页面;
  5. 浏览器在代码中发现一个< img >标签引用了一张图片,向服务器发出请求。此时浏览器不会等到图片下载完,而是继续渲染后面的代码;
  6. 服务器返回图片文件,因为图片占用了必定面积,影响了后面段落的排布,所以浏览器须要回过头来从新渲染这部分代码;
  7. 浏览器发现了一个包含一行 Javascript 代码的< script >标签,直接运行;
  8. Javascript 脚本操做某个元素,它命令浏览器隐藏掉代码中的某个标签 (style.display=”none”)。浏览器会从新渲染这部分代码;
  9. 遇到 </html >,流程结束;
  10. 当用户操做,页面产生交互,浏览器发现某个部分发生了点变化影响了布局,浏览器回流(reflow);改变某个元素的背景色、文字颜色、边框颜色等等不影响它周围或内部布局的属性时,浏览器重绘(repaint)。

HTML 页面加载优化

  • 页面减肥。页面的肥瘦是影响加载速度最重要的因素删除没必要要的空格、注释。将 inline 的 script 和 css 移到外部文件,可使用 HTML Tidy 来给 HTML 减肥,还可使用一些压缩工具来给 JavaScript 减肥;

  • 减小文件数量。减小页面上引用的文件数量能够减小 HTTP 链接数。许多 JavaScript、CSS 文件能够合并最好合并;

  • 减小域名查询。DNS 查询和解析域名也是消耗时间的,因此要减小对外部 JavaScript、CSS、图片等资源的引用,不一样域名的使用越少越好;

  • 缓存重用数据;

  • 优化页面元素加载顺序。首先加载页面最初显示的内容和与之相关的 JavaScript 和 CSS,而后加载 DHTML 相关的东西,像什么不是最初显示相关的图片、flash、视频等很肥的资源就最后加载;

  • 减小 inline JavaScript 的数量。浏览器 parser 会假设 inline JavaScript 会改变页面结构,因此使用 inline JavaScript 开销较大,不要使用 document.write()这种输出内容的方法,使用现代 W3C DOM 方法来为现代浏览器处理页面内容;

  • 使用现代 CSS 和合法的标签。使用现代 CSS 来减小标签和图像,例如使用现代 CSS+文字彻底能够替代一些只有文字的图片,使用合法的标签避免浏览器解析 HTML 时作“error correction”等操做,还能够被 HTML Tidy 来给 HTML 减肥;

  • 不要使用嵌套 tables;

  • 指定图像和 tables 的大小。若是浏览器能够当即决定图像或 tables 的大小,那么它就能够立刻显示页面而不要从新作一些布局安排的工做,这不只加快了页面的显示,也预防了页面完成加载后布局的一些不当的改变。

参考文章

  1. 网页在浏览器上的渲染过程
  2. 性能优化——CSS和JS的加载和执行
  3. 浏览器渲染原理及流程