HTML页面重绘和重排

193 阅读6分钟

用户界面---浏览器引擎(数据存储持久层)---渲染引擎(浏览器的内核)(网络、js解释器)

一、从其他层面理解浏览器的结构:浏览器是个多进程结构。

  • 浏览器进程:控制除标签页外的用户界面,包括地址,书签,后退,前进按钮等,以及负责与浏览器其他进程负责协调工作
  • 缓存进程
  • 网络进程:负责发起接收网络请求
  • 渲染器进程:用于控制显示tab标签内的所有内容,浏览器在默认情况下会为每个标签页创建一个渲染器进程
  • GPU进程:负责整个浏览器页面的渲染
  • 插件进程:控制网站使用的所有插件

二、给浏览器输入一个网址到渲染到整个页面的过程:

  • 浏览器进程的ui线程会捕捉输入内容,ui线程会启动一个网络进程请求DNS进行域名解析,接着开始连接服务器获取数据。
  • 当网络线程获取到数据后会通过SafeBrowsing来检查站点是否是恶意站点,如果是,浏览器弹出警示,当然也可以强行访问
  • 当返回数据准备完毕并且安全校验通过时,UI线程会创建一个渲染器进程来渲染页面。浏览器进程通过IPC管道(进程之间的通信管道)将数据传递给渲染器进程,正式进入渲染器进程。
  • 渲染器进程中的工作
    • 渲染器的主线程将HTML(也就是数据)进行解析,构造DOM数据结构(DOM树)——》HTML首先经过tokeniser标记化通过词法分析将输入的HTML内容解析成多个标记,根据识别后的DOM标记进行DOM树构造,在DOM树构造中创建document对象,以document为根节点的DOM树不断进行修改,向其中添加各种元素,在HTML解析完成后我们就会得到一个DOM Tree
    • 主线程解析CSS并确定每个DOM节点的计算样式
    • layout布局:确定每个节点的在页面上的坐标及该节点占据多大的区域——》主线程通过遍历DOM和计算好的样式来生成layout树,layout上的每个节点都记录坐标和边框尺寸。注意:DOM树和layout树不是一一对应的,这是因为一个不包含计算样式,一个包含。layout tree和最后展示在界面上的节点是一一对应的
    • 绘制:主线程遍历layout tree创建绘制顺序表(paint record)该表记录了绘制的顺序
    • 栅格化:将所有信息转化为像素点显示在屏幕上——》主线程遍历layout tree生成layer tree并连同绘制信息传递给合成器线程,合成器线程将每个图层切分为很多个图块,将每个图块发送给栅格化线程,栅格化线程栅格化每个图块并将他们存储到GPU内存中 ,当图块栅格化完成后合成器线程又收集“draw quards”图块信息,这些信息里记录了图块在内存中的位置和在页面的哪个位置绘制图块,根据信息合成器线程生成了合成器帧,该合成器帧通过ipc传送给浏览器进程,接着又传送给GPU,然后GPU渲染到屏幕上。
      当页面发生变化,例如滚动,则会生成一个新的合成器帧再进行相应的操作进行传递渲染。

三、重绘和重排:

  • 重排(回流/重构)是指当渲染树中的一部分因为元素的规模尺寸,布局,隐藏等改变而需要重新构建。
    当我们改变一个元素的尺寸位置属性时会重新进行样式计算、布局、绘制已经后面所有的流程,这种行为称为重排
    触发重排的条件:任何页面布局和几何属性的改变,如页面渲染初始化、添加或删除可见的DOM元素、元素位置的改变或使用动画、元素尺寸的改变(大小、边框、外边距)、浏览器窗口的尺寸变化、填充内容的变化(文本的改变、图片大小的改变)
  • 重绘是指一个元素外观的改变所触发的浏览器行为,浏览器会根据元素的新属性重新绘制,使元素呈现新的外观。
    当我们改变某个元素的颜色属性时,不会重新触发布局,但还是会触发样式计算和绘制,这种行为称为重绘
    触发重绘条件:改变元素的外观属性,如color、background-color
  • 在重排时,浏览器会使渲染器中受到影响的部分失效,并重新构造这部分渲染树,完成重排后,浏览器会重新绘制受到影响的部分到屏幕中完成重绘。重排必定引发重绘,重绘不一定引发重排
  • 重排重绘导致的问题----耗时、卡顿
    重排和重绘都会占用主线程,js也运行在主线程,会出现抢占时间的问题。
    如果有一个不断导致重排重绘的动画,浏览器则每一帧都运行样式、计算布局和绘制的操作。当页面以每秒60帧的刷新率才不会让用户觉得卡顿,如果在运行动画时还有大量的js任务需要执行,若js执行时间过长会导致在下一帧开始时js没有及时归还主线程,就导致下一秒动画没有按时渲染出现页面动画卡顿。
  • 优化重排重绘的方法
    • requestAnimationFrame()方法会在每一帧被调用,通过API的回调可以把JS任务分成更小的任务块,在每一帧时间用完前暂停JS执行归还主线程。如此一来,主线程就可以按时执行布局和绘制。React渲染引擎React Fiber就使用了该API做了很多优化

    • 栅格线程和合成器线程不使用主线程,无需和JS抢夺主线程。CSS中的动画属性transform不会经过布局和绘制,而是直接运行在合成器线程和栅格线程中不会受到主线程中JS执行的影响。更重要的事,由于transform实现的动画不需要经过布局、绘制、样式计算等操作节省了很多运算时间。