现代浏览器网页渲染原理简介

0.为什么要理解浏览器的工作原理

  • 为了写出更好的代码和提供更好的用户体验

简化的浏览器结构图:

  • 用户界面  
    • 用于展示除标签页窗口之外的其他用户界面的内容
  • 浏览器引擎
    • 用于再用户界面和渲染引擎之间传递数据
  • 渲染引擎(重点,常称为浏览器的内核)
    • 负责渲染用户请求的页面内容
    • 下面还有很多小的功能模块 

内核使用:

 

 

 Blink

1.目前的浏览器是个多进程结构

  1. 浏览器进程:控制除标签页外的用户界面,包括地址,书签,后退,前进按钮等,以及负责与浏览器其他进程负责协调工作
  2. 缓存进程:Cookie、sessionStorage。。。
  3. 网络进程:发起网络请求
  4. 渲染器进程:控制每个Tab标签内的显示内容。默认情况下浏览器可能会为每个标签页都创建一个进程
    1. 可能:是与打开Chrome时选择的进程模型有关。chromium的官方文档中说明了chrome一共有4种进程模型
  5. GPU进程:渲染
  6. 插件进程:内置插件

早期的浏览器是一个单进程结构:

如一个浏览器进程中:

  • 页面线程负责页面
  • JS线程负责JS代码
  • 以及其他线程

因此会出现很多问题,如:

  • 不稳定:一个线程的卡死可能导致整个进程出现问题。如打开多个标签页,其中一个标签页卡死,可能会导致整个浏览器无法正常运行
  • 不安全:浏览器之间的线程是可以互通数据的,可能会通过JS访问其他线程的数据
  • 不流畅:运行效率低

2.前端层面输入一个地址然后按回车经历的过程

(1)数据请求部分

 

 (2)数据处理部分

(chrome)当网络线程获取到数据后,会通过SafeBrowsing(是谷歌内部的一套站点安全系统,通过检测该站点的数据是否安全,如看该IP是否在谷歌的黑名单内)来检查站点是否是恶意站点。如果是则显示警告页面,但还是可以继续访问。

    • 当返回数据下载完毕并且安全校验通过时,网络线程会通知UI线程已经准备好了。
    • UI线程会创建一个:
      • 渲染器进程(Renderer Thread)来渲染页面:把html,css,js,image等资源渲染成用户可以交互的web页面。
        • 浏览器进程通过IPC管道将数据传递给渲染器进程,进入渲染流程:
        • 渲染器进程的主线程将html进行解析,构造DOM数据结构
          • html首先经过Tokeniser标记化,通过词法分析,将输入的html内容解析成多个标记,根据解析后的标记进行DOM树构造
          • 在DOM树构造过程中会创建document对象,以document为根节点的DOM树不断修改,向其中添加各种元素。
          • html代码中往往会引入其他资源,如图片、CSS、js等:
            • 图片和CSS资源需要通过网络下载或者从缓存中直接加载,这些资源不会阻塞html的解析,因为它们不会影响DOM的生成
            • 但是当HTML解析过程中遇到script标签就会停止html解析流程,转而去加载解析并执行JS(因为JS可能会改变当前页面的HTML结构)
    • Layout布局:在知道DOM节点和每个节点的样式后,接下来需要知道每个节点需要放到页面上的哪个位置(坐标及占用区域)。这个阶段称为Layout布局:
      • 主线程通过遍历dom和计算好的样式来生成Layout Tree。Layout Tree上的每个节点都记录了x,y坐标和边框尺寸
    •  绘制(paint):(绘制记录表)
      • 我们需要知道以什么样的顺序绘制。为了保证在屏幕上展示正确的层级。
      • 主线程遍历layout tree创建一个绘制记录表。该表记录了绘制的顺序。这个阶段被称为绘制
    • 合成(Composting)
      • 知道了绘制顺序,可以把这些信息转化为像素点显示在屏幕上的时候了
      • 早期的chrome栅格化会导致展示延迟
      • 目前的处理方案是合成。
        • 合成是一种将页面上各个部分分成多个图层,分别对其栅格化,并在合成器线程(Compositor Thread)中单独进行页面合成的技术。
        • 即:把页面所有的元素按照某种既定的规则进行分图层。并把图层都栅格化好了。然后只需要把可视区的内容组合成一帧展示给用户即可。

 

        • 由于一层可能像页面的整个长度一样大,因此合成器线程将他们切分为多个图块(tiles)。
        • 再将每个图块发送给栅格化线程,栅格线程栅格每个图块,并将其存储在GPU内存中
        • 当图片栅格化完成后,合成器线程将收集称为draw quads的图块信息(记录了图块在内存中的位置和在页面中的哪个位置绘制图块的信息)
        • 通过图块信息,合成器线程生成了一个合成器帧,然后合成器帧通过IPC传送给浏览器进程
        • 浏览器进程将合成器帧传送到GPU,GPU渲染展示到屏幕上  
          • 此时如果页面发生变化,如滚动了页面,都会生成一个新的合成器帧
          • 新的帧再传给GPU,然后再次渲染到屏幕上
    注意:
    1. Layout Tree和Dom tree并不是一一对应的
    2. 如设置了display:none。的节点不会出现在Layout tree 上,而在before伪类中添加了content值的元素,content里的内容会出现在Layout tree上,不会出现在DOM树里。
    3. 这是因为DOM是通过HTML解析获得,并不关心样式。而layout tree是通过DOM树和计算好的样式来生成。layout tree是和最后展示在屏幕上的节点是对应的

 3.汇总:

  • 浏览器进程中的网络线程请求获取到html数据后,通过IPC(进程间通信管道)将数据传给渲染器进程的主线程
  • 主线程将html解析构造DOM树,然后进行样式计算。根据DOM树和生成好的样式生成Layout Tree
  • 通过遍历Layout Tree生成绘制顺序表,接着生成Layer tree。
  • 然后主线程将Layer Tree和绘制顺序信息一起传给合成器线程。
  • 合成器线程按规则进行分图层,并把图层分为更小的图块(tiles)传给栅格线程进行栅格化
  • 栅格化完成后,合成器线程会获得栅格线程传过来的"draw quads"图块信息
  • 根据这些信息,合成器线程上合成了一个合成器帧,然后将该合成器帧通过IPC传回给浏览器进程
  • 浏览器进程再传到GPU进行渲染。这样就展示到屏幕上了
  • 重排:当我们改变一个元素的尺寸位置属性时,会重新进行样式计算(Computed Style),布局(Layout)绘制(Paint)以及后面的所有流程
  • 重绘:当我们改变某个元素的颜色属性时,不会重新触发布局,但还是会触发样式计算和绘制。
  • 说明:
    1. 重排和重绘都会占用主线程,JS也会运行在主线程上,因此会出现抢占执行时间的问题
    2. 当页面以每秒60帧的刷新率才不会感觉到卡顿。如果在运行动画时还有大量的JS任务需要执行。因为布局、绘制和JS执行都是在主线程运行的
    3. 当在一帧的时间内布局和绘制结束后,还有剩余时间,JS就会拿到主线程的使用权,如果JS执行时间过长就会导致在下一帧开始时JS没有及时归还主线程,导致下一帧动画没有按时渲染,就会出现卡顿
    4. 处理:
      1. requestAnimationFrame()
      2. 使用Transfrom:通过Transform实现的动画不需要经过布局绘制、样式计算等操作。所以实现了很多运算时间

 4.动画卡顿的原因

例如用JS写了一个不断重排重绘的动画,浏览器需要在每一帧都运行样式计算布局和绘制的操作

  • 如果在运行动画时还有大量的JS任务需要执行,由于布局、绘制、JS执行都是在主线程执行的
  • 当在一帧的时间内布局和绘制结束后还有剩余时间,JS就会拿到主线程的使用权
  • 如果JS执行的时间过长,就会导致在下一帧开始时JS没有及时归还主线程,导致下一帧动画没有按时渲染,就会导致卡顿
  • 解决:
    • requestAnimationFrame():会在每一帧被调用,可以把JS任务分成一些更小的任务快,在每一帧时间用完前暂停JS执行归还主线程——React Fiber
    • 栅格化的过程是不占用主线程的,只在合成器线程和栅格线程中运行——transform实现的动画不会通过布局和绘制,而是直接运行在合成器线程和栅格化线程中,不会影响主线程JS的执行

 

(1)影响回流的操作

  • 添加/删除元素
  • 操作styles
  • display:none
  • offsetLeft,scrollTop,clientWidth
  • 移动元素的位置
  • 修改浏览器的大小,字体大小

 (2)避免layout thrashing——布局抖动

回流的时候可能会出现layout thrashing问题:因为强制不断的回流的发生

  • 避免回流
    • CSS方面:如用translate实现位移等
    • 减少回流:如React的virtual DOM减少回流的发生
  • 读写分离

 

posted @ 2020-11-17 11:31  俄罗斯方块  阅读(232)  评论(0编辑  收藏  举报