浏览器的渲染机制

2019/04/10 10:10
阅读数 11

 

 

作者:小土豆biubiubiu

博客园:www.cnblogs.com/HouJiao/

掘金:https://juejin.im/user/58c61b4361ff4b005d9e894d

简书:https://www.jianshu.com/u/cb1c3884e6d5

微信公众号:土豆妈的碎碎念(扫码关注,一起吸猫,一起听故事,一起学习前端技术)

码字不易,点赞鼓励哟~

一.前言

  我们熟知的浏览器的主要功能就是向服务器发送请求,然后显示服务器返回的资源。

  而这里的资源一般指的就是HTML文档。

  (当然资源还包括xml、图片)

  那浏览器是如何将一个HTML文档解析为我们眼睛看到的各种图形和颜色呢。

  这便是我们要讨论的浏览器的渲染机制了。

  关于浏览器的渲染机制,是面试会被问到的高频考点。

  所以话不多说,一起探究一下这个问题。

 

  备注:浏览器的渲染机制是由浏览器的渲染引擎决定的。

     浏览器的渲染引擎也就是我们常说的浏览器的内核。

     比如常见的IE的内核-Trident,Firefox的内核-Gecko,chrome的内核-Webkit等。

     由于不同浏览器的内核实现存在差异,因此同一份HTML文档在不同浏览器上的解析结果也会有细微的差距。

     本篇文章不会细致到不同内核的差异。

 

二.浏览器的渲染线程

  我们都知道一份HTML文档中一般会包含HTML标签、CSS样式、JavaScript脚本这些基础的内容。

  所以浏览器为了处理HTML文档,设计了一系列的线程,分别为:

    GUI渲染线程

    JS引擎线程

    定时触发器线程

    事件处理线程

    异步http请求线程

  下面简单的了解一下这些线程在解析HTML文档中都分别负责那些任务。

 

GUI渲染线程

  GUI-图形用户界面,那GUI渲染线程也就是负责渲染浏览器界面上显示的内容。

  即负责解析HTML标签和CSS样式。

  在浏览器的五个渲染流程中,GUI渲染线程均参与其中。

 

JS引擎线程

  JS引擎线程顾名思义就是负责处理JavaScript脚本的线程。

  我们都知道JS存在同步任务和异步任务。

  而JS引擎线程是一个单线程所以JS引擎在执行JavaScript代码的时候主线程会维护一个执行栈除了执行栈之外还会维护一个任务队列。

  主线程的执行栈执行同步任务,当遇到一些异步任务时,先将异步任务交给对应的线程。

  当异步任务满足条件需要执行时,会将其推入任务队列,主线程空闲后会顺序处理任务队列中的任务。

  (这个执行机制称为事件循环机制)

  备注:对于不同内核的浏览器,对应的JS引擎也不同。

     IE8的JS引擎是Jscript,IE9开始用Chakra。

     FireFox不同版本的JS引擎分别为SpiderMonkey(1.0-3.0)/ TraceMonkey(3.5-3.6)/ JaegerMonkey(4.0)。

     Chrome的JS引擎是V8引擎。

 

事件处理线程

  JS引擎线程遇到事件处理程序代码块时,会将这个处理程序添加到事件处理线程中。

  当事件满足条件被触发后,事件处理线程会将该事件添加到前面说的JS执行时维护的任务队列中,之后就交由JavaScript引擎线程去处理。

 

定时触发器线程

  处理setTimeout/setInterval代码的线程就叫定时触发线程。

  当定时器被触发后,会将需要执行的任务添加到JS的任务队列,当JS引擎线程空闲的时候,会按顺序执行任务队列中的任务。

  (这里牵扯到的一个知识点就是定时器被触发后不会立即就执行我们编写的定时任务,必须要等到JS引擎空闲且按顺序执行任务队列中的任务)

  

异步HTTP请求线程

  异步HTTP请求线程主要用于处理XMLHttpRequest请求。

  当我们的程序需要向服务器发起请求时,就会交给该线程处理。

  当请求得到响应后,如果有需要执行的回调函数,会将回调放入JS的任务队列,后续在由JS引擎线程处理

   

  基于前面对这些线程的简单描述,我画了一个简单的图来解释这些线程的工作

  

 

三.浏览器的渲染流程

  简单了解过浏览器渲染过程中运行的几个线程之后,接着我们就需要详细去了解浏览器的渲染流程。

   

解析构建DOM树(DOM Tree

  解析意为解释分析,即将文档中的代码转化为浏览器引擎可以理解的规则或者数据结构。

  那解析后的结果是代表文档结构的一个树,一般将其称为解析树或者语法树

   

  我们知道一份HTML文档是由html标签、CSS规则和JavaScript脚本构成的。

  所以在DOM树的构建过程中,分别会遇到HTML解析、CSS解析和JavaScript脚本解析(或者外部样式、脚本加载)。

  我们以下面这份HTML文档为例,简单的梳理一下文档解析为DOM树的过程。

<html>
    <head>
        <meta chartset="utf-8">
        <title>浏览器渲染流程</title>
        <style type="text/css">
            .box{
                border:1px solid #448aff;
            }
            .box span{
                width: 100px;
                height: 100px;
                border-radius: 50%;
            }
        </style>
        <script type="text/javascript" src="./index.js"></script>
    </head>
    <body>
        <div class="box">
            <span class="cicle"></span>
        </div>
    </body>
</html>

 

  

  

  

  构建DOM树的过程中,我们需要注意一下几点:

    GUI渲染引擎在处理HTML标签的同时,也会同步进行CSS规则树的构建

    GUI渲染引擎和JS引擎是互斥的,两者不能同时进行。当有一个正在运行时,另外一个就需要挂起。

  

构建CSS规则树

  构建CSS规则树的过程就是处理CSS样式的过程,构建CSS规则树是由CSS解释器来完成的。

  CSS解释器会将CSS文件解析为一个StyleSheet对象,StyleSheet对象又包含一个或者多个CSSRule对象。

  CSSRule对象则包含选择器对象Selectors声明对象Declaration

  那基于前面文档中的CSS样式,我们按照规则简单构建一个CSS规则树。

  

  

  CSS解释器构建CSS规则树这里我需要总结几点:

    CSS解释器会复合的CSS规则拆分成多个声明对象(可以参考上图中border的解析)

    CSS规则树的构建和DOM树的构建是同步进行的

 

呈现树构建(Render Tree)

  呈现树基于DOM树和CSS规则树构建的,是文档的可视化表示。

  它的作用是为了后续能将元素进行正确的布局和绘制。

  在呈现树的构建过程中,总结了以下几点:

    一些非可视化的元素不会插入到呈现树中,比如head元素、display为none的元素。

    呈现树构建时会计算每一个可视化元素的样式属性。

    样式属性计算就是为每一个元素查找匹配的CSS规则,这个就是根据我们写的CSS选择器进行匹配的。

 

布局(Layout)

  呈现树构建只是基于DOM树和CSS规则为元素匹配出了对应的样式属性,但是并不会计算出元素在屏幕上从位置和大小。

  所以接下来就需要计算元素的位置和大小,我们将其称为布局。

  布局阶段会遍历呈现树,根据呈现树中每一个节点的信息(宽、高等样式属性)准确计算出每一个节点在页面上的位置和大小。

 

绘制(Paint)

  绘制阶段,浏览器引擎会根据呈现树以及布局计算出来的元素位置和大小,将元素显示到屏幕上。

 

四.总结

  到此,浏览器中的渲染机制就梳理完成了,基本都是概念性的内容,因此我们在对渲染流程进行一个总结:

    解析构建DOM树

    构建CSS规则树

    根据DOM树和CSS规则树构建呈现树

    根据呈现树进行布局

    根据呈现树和布局计算出来的位置和大小将元素绘制到浏览器中

  除了渲染流程,我们还需要关注前面每个过程中需要关注的点。

  

  备注:

    在这里我们需要理解GUI渲染引擎和JS引擎的互斥关系。

    我们都知道JS代码是可以操作DOM和CSS样式的。

    假如GUI渲染引擎一直持续不断的构建DOM树、规则树、呈现树以及后续的布局和绘制。

    那么我们JS操作样式和DOM的逻辑显然不会在页面上生效。

 

五.重排和重绘

  在最后呢,我们在补充一下关于页面渲染过程中会出现的重排和重绘现象。

  先来简单了解一下这两个概念。

  重绘(repaint):界面中有部分元素的外观发生了变化,

              比如:背景颜色,字体颜色,那么浏览器需要将该元素的新属性重新显示到浏览器上,这个叫做重绘。

  重排(reflow):重排也叫重构或者回流。

          它指的是界面中有部分元素的尺寸,布局发生了变化,那么就会导致发生变化的这部分呈现树重新构建,这叫做浏览器的重排。

             新的呈现树构建完成后,浏览器需要重新布局、绘制这部分发生改变的呈现树到浏览器屏幕上(重排后需要进行重绘)。

 

  前面我们说过,呈现树是基于DOM树和CSS规则树的,在这里浏览器会有一个规则叫:CSS阻塞呈现树。

  因为假如我们在DOM树和CSS规则树构建的同时去构建呈现树,那可想而知在后续解析CSS样式时会发生很多次的重排和重绘。

  关于重排和重绘就简单的了解到这里,这两个现象是不可避免发生的,也不可避免的会造成页面性能上从损失。

  因此后续会有文章详细解释浏览器的重排和重绘,并且给出优化建议。

  

  

作者:小土豆biubiubiu

博客园:www.cnblogs.com/HouJiao/

掘金:https://juejin.im/user/58c61b4361ff4b005d9e894d

简书:https://www.jianshu.com/u/cb1c3884e6d5

微信公众号:土豆妈的碎碎念(扫码关注,一起吸猫,一起听故事,一起学习前端技术)

码字不易,点赞鼓励哟~

 

原文出处:https://www.cnblogs.com/HouJiao/p/12482221.html

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部