乐趣区

关于前端:浏览器渲染原理一文搞懂

浏览器渲染原理

前言

浏览器的次要性能总结起来就是一句话:将用户输出的 URL 转变成可视化的图像。

  1. URLDOM 树;
  2. DOM 树到可视化图像;
  3. 这两个过程之间的关系并没有那么明确,咱们能够统称这两个过程为页面的渲染;

1. 网页的解析过程

  • 思考一下:一个网页从 url 输出到浏览器,经验过怎么的解析过程?
  • 要想深刻了解下载的过程,咱们还要先了解,一个 index.html 被下载下来后是如何被解析和显示在浏览器上的;

2. 浏览器的性能与组成

2.1 浏览器内核

  1. Trident(三叉戟):IE、360 平安浏览器、UC 浏览器
  2. Gecko(壁虎):Mozilla Firefox
  3. Presto(急板乐曲)-> Blink(眨眼):Opera
  4. Webkit:Safari、360 极速浏览器、搜狗高速浏览器、挪动端浏览器(Android、iOS)
  5. Webkit -> Blink:Google Chrome,Edge

浏览器的内核相当于汽车的发动机,是最外围的存在,它负责将代码转换成用户眼中的界面。

  • 咱们常常说的浏览器内核指的是浏览器的排版引擎:

    • 排版引擎 (layout engine),也称为浏览器引擎 (browser engine)、页面渲染引擎 (rendering engine) 或样版引擎;
  • 也就是一个网页下载下来后,就是由咱们的渲染引擎来帮忙咱们解析的;

2.2 过程与线程

  • 过程与线程的解释:

    • 过程:程序的一次执行,它占有一片独有的内存空间,是操作系统执行的根本单元;
    • 线程:是过程内的一个独立执行单元,是 CPU 调度的最小单元;
  • 浏览器:多过程、多线程模型

    • Browser 过程:

      • 浏览器的主过程,负责浏览器界面的显示,和各个页面的治理;
      • 浏览器中所有其余类型过程的先人,负责其余过程的的创立和销毁,它有且只有一个!
    • Renderer 过程:

      • 网页渲染过程,负责页面的渲染,能够有多个渲染过程的数量,不肯定是关上页面的数量;
    • GPU Process:负责 GPU 相干;
    • Plugin Process:负责管制一个网页用到的 Plugin

3. 浏览器渲染流程

3.1 渲染引擎解析过程

  • 渲染引擎在拿到一个页面后,如何解析整个页面并且最终呈现出咱们的网页呢?

    • 咱们通过上面的这幅图,让咱们更加具体的学习它的过程;

3.2 渲染引擎次要模块

  • 一个渲染引擎次要包含:HTML 解析器,CSS 解析器,javascript 引擎,布局 layout 模块,绘图模块;

    • HTML 解析器:解释 HTML 文档的解析器,次要作用是将 HTML 文本解释成 DOM 树;
    • CSS 解析器:它的作用是为 DOM 中的各个元素对象计算出款式信息,为布局提供基础设施;
    • Javascript 引擎:应用 Javascript 代码能够批改网页的内容,也能批改 css 的信息,javascript 引擎可能解释 javascript 代码,并通过 DOM 接口和 CSS 树接口来批改网页内容和款式信息,从而扭转渲染的后果
    • 布局 (layout):在 DOM 创立之后,Webkit 须要将其中的元素对象同样式信息联合起来,计算他们的大小地位等布局信息,造成一个能表白这所有信息的外部示意模型
    • 绘图模块 (paint):应用图形库将布局计算后的各个网页的节点绘制成图像后果

4. 渲染页面的具体流程

浏览器渲染页面的整个过程:浏览器会从上到下解析文档。

4.1 HTML 解析过程

遇见 HTML 标记,调用 HTML 解析器解析为对应的 token(一个 token 就是一个标签文本的序列化)并构建 DOM 树(就是一块内存,保留着 tokens,建设它们之间的关系)

4.2 生成 CSS 规定

遇见 style/link 标记调用解析器解决 CSS 标记并构建 CSS 款式树。

4.3 构建 Render Tree

当有了 DOM Tree CSSOM Tree 后,就能够两个联合来构建 Render Tree

  • 留神一link 元素不会阻塞 DOM Tree 的构建过程,然而会阻塞 Render Tree 的构建过程

    • 这是因为 Render Tree 在构建时,须要对应的 CSSOM Tree
  • 留神二Render TreeDOM Tree 并不是一一对应的关系

    • 比方对于 displaynone 的元素,压根不会呈现在 render tree 中;

4.4 布局 (layout) 和绘制 (Paint)

  • 第四步是:在渲染树(Render Tree)上运行 布局(Layout) 来计算每个节点的几何体。

    • 渲染树会示意显示哪些节点以及其余款式,然而不示意每个节点的尺寸、地位等信息;
    • 布局是确定出现树中所有节点的宽度、高度和地位信息;
  • 第五步是:将每个节点绘制(Paint)到屏幕上。

    • 在绘制阶段,浏览器将布局阶段计算的每个 frame 转为屏幕上理论的像素点;
    • 包含将元素的可见局部进行绘制,比方文本、色彩、边框、暗影、替换元素(比方 img)

5. 重绘和回流解析

5.1 重绘(Repaint)

  • 第一次渲染内容称之为绘制(paint)

    • 之后从新渲染称之为重绘
  • 重绘不会带来从新布局,所以并不一定随同重排。

    • 浏览器会依据元素的新属性从新绘制,使元素出现新的外观。
  • 什么属性会导致重绘呢?

    • color background box-shadow.. (比方批改背景色、文字色彩、边框色彩、款式等)

5.2 回流 / 重排(reflow)

了解回流 reflow:也能够称之为重排

  • 第一次确定节点的大小和地位,称之为布局(layout)

    • 之后对节点的大小、地位批改从新计算称之为回流
  • “ 重排 / 回流 ” 必然导致 ” 重绘 ”

    • 比方扭转一个网页元素的地位,就会同时触发 ” 重排 ” 和 ” 重绘 ”,因为布局扭转了
    • 所以 回流是一件很耗费性能的事件
  • 什么属性会导致回流呢?

    • width top position..(比方 DOM 构造产生扭转 / 批改了布局)

5.3 常见的触发 ” 回流 / 重排 ” 操作

  • Reflow 的老本比 Repaint 的老本高得多的多。
  • 所以在开发中要尽量避免产生回流:

    1. 批改款式时尽量一次性批改

      • 将屡次 扭转款式属性的操作合并成一次操作
      • 事后定义好 class,而后批改 DOM 的 className
    2. 尽量避免频繁的操作 DOM

      • 咱们能够在一个 DocumentFragment 或者父元素中将要操作的 DOM 操作实现,再一次性的操作;
    3. 尽量避免通过 getComputedStyle 获取尺寸、地位等信息;
    4. 对某些元素应用 positionabsolute 或者 fixed

      • 并不是不会引起回流,而是开销绝对较小,不会对其余元素造成影响。

5.4 合成和性能优化

  • 绘制的过程,能够将布局后的元素绘制到多个合成图层中

    • 这是浏览器的一种优化伎俩
  • 默认状况下,规范流中的内容都是被绘制在同一个图层(Layer)中的
  • 而一些非凡的属性,会创立一个新的合成层(CompositingLayer),并且新的图层能够利用 GPU 来减速绘制

    • 因为 每个合成层都是独自渲染
  • 那么哪些属性能够造成新的合成层呢? 常见的一些属性:

    • 3D transforms
    • video、canvas、iframe
    • opacity 动画转换时
    • position: fixed
    • will-change:一个实验性的属性,提前通知浏览器元素可能产生哪些变动
    • animation 或 transition 设置了 opacity、transform
  • 分层的确能够进步性能,然而它以内存治理为代价,因而不应作为 web 性能优化策略的一部分适度应用

5.5 script 元素和页面解析的关系

  • 咱们当初曾经晓得了页面的渲染过程,然而 JavaScript 在哪里呢?

    • 事实上,浏览器在解析 HTML 的过程中,遇到了 script 元素是不能持续构建 DOM 树的;
    • 它会进行持续构建,首先下载 JavaScript 代码,并且执行 JavaScript 的脚本;
    • 只有等到 JavaScript 脚本执行完结后,才会持续解析 HTML,构建 DOM 树;
  • 为什么要这样做呢?

    • 这是因为 JavaScript 的作用之一就是操作 DOM,并且能够批改 DOM
    • 如果咱们等到 DOM 树构建实现并且渲染再执行 JavaScript,会造成重大的回流和重绘,影响页面的性能;
    • 所以会在遇到 script 元素时,优先下载和执行 JavaScript 代码,再持续构建 DOM 树;
  • 然而这个也往往会带来新的问题,特地是古代页面开发中:

    • 在目前的开发模式中(比方 VueReact),脚本往往比 HTML 页面更“重”,解决工夫须要更长;
    • 所以会造成页面的解析阻塞,在脚本下载、执行实现之前,用户在界面上什么都看不到;
  • 为了解决这个问题,script 元素给咱们提供了两个属性(attribute):deferasync

6. defer 和 async 属性

6.1 defer 属性

  • defer 属性:通知浏览器 不要期待脚本下载 ,而 持续解析 HTML构建 DOM Tree

    • 脚本会由浏览器来进行下载,然而不会阻塞 DOM Tree 的构建过程;
    • 如果脚本提前下载好了,它会期待 DOM Tree 构建实现,在 DOMContentLoaded 事件之前先执行 defer 中的代码;
  • 所以 DOMContentLoaded 总是会期待 defer 中的代码先执行实现
  • 另外多个带 defer 的脚本是能够放弃正确的程序执行的。

    • 从某种角度来说,defer 能够进步页面的性能,并且举荐放到 head 元素中;
    • 留神:defer 仅实用于内部脚本,对于 script 默认内容会被疏忽。

6.2 async 属性

  • sync 个性与 defer 有些相似,它也可能让脚本不阻塞页面
  • async 是让一个脚本齐全独立的:

    • 浏览器不会因 async 脚本而阻塞(与 defer 相似);
    • async 脚本不能保障程序,它是独立下载、独立运行,不会期待其余脚本;
    • async 不会能保障在 DOMContentLoaded 之前或者之后执行
  • defer 通常用于须要在文档解析后操作 DOM 的 JavaScript 代码,并且对多个 script 文件有程序要求的;

    • async 通常用于独立的脚本,对其余脚本,甚至 DOM 没有依赖的;
退出移动版