前言

最近都在跟着青训营学习,不跟着学习的时候也去看node的相关课程了,很少有自己去学一些别的知识,刚好今天的学习任务量较少,进度赶出来了,晚上无聊,看了看之前感兴趣的重绘重排的相关知识,梳理了一下浏览器的相关原理,挑出一部分,记录在博客上。

纯记录

渲染进程

渲染进程的核心工作是将HTML,CSS和JavaScript转换为用户可以与之交互的网页。主要包括以下线程:

浏览器 GUI 渲染线程

渲染流程

image-20210615171514782

  • 分层的目的:避免整个页面渲染,把页面分成多个图层,尤其是动画的时候,把动画独立出一个图层,渲染时只渲染该图层就ok,transform,z-index等,浏览器会自动优化生成图层
  • 光栅化:页面如果很长但是可视区很小,避免渲染非可视区的样式造成资源浪费,x以将每个图层又划分成多个小个子,当前只渲染可视区附近区域
重排
  • 重排 :当DOM的变化影响了元素的几何信息(元素的的位置和尺寸大小),浏览器需要重新计算元素的几何属性,将其安放在界面中的正确位置,这个过程叫做重排

  • 重排特点:style后面所有流程都更新

image-20210615172629674

  • 触发重排的方法

    • 页面初始渲染,这是开销最大的一次重排

    • 添加/删除可见的DOM元素

    • 改变元素位置

    • 改变元素尺寸,比如边距、填充、边框、宽度和高度等

    • 改变元素内容,比如文字数量,图片大小等

    • 改变元素字体大小

    • 改变浏览器窗口尺寸,比如resize事件发生时

    • 激活CSS伪类(例如::hover

    • 设置 style 属性的值,因为通过设置style属性改变结点样式的话,每一次设置都会触发一次reflow

      查询某些属性或调用某些计算方法:offsetWidth、offsetHeight等

重绘
  • 重绘:当一个元素的外观发生改变,但没有改变布局,重新把元素外观绘制出来的过程

  • 重绘特点:跳过布局和分层阶段

image-20210615172906367

  • 重排必重绘
避免重排的方法
  • 样式集中改变

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // bad
    var left = 10;
    var top = 10;
    el.style.left = left + "px";
    el.style.top = top + "px";

    // 当top和left的值是动态计算而成时...
    // better
    el.style.cssText += "; left: " + left + "px; top: " + top + "px;";

    // better
    el.className += " className";
  • 使用 absolute 或 fixed 脱离文档流

    使用绝对定位会使的该元素单独成为渲染树中 body 的一个子元素,重排开销比较小,不会对其它节点造成太多影响。当你在这些节点上放置这个元素时,一些其它在这个区域内的节点可能需要重绘,但是不需要重排

  • GPU加速:transform

    1
    2
    3
    4
    5
    6
    7
    8
    9
    /*
    * 根据上面的结论
    * 将 2d transform 换成 3d
    * 就可以强制开启 GPU 加速
    * 提高动画性能
    */
    div {
    transform: translate3d(10px, 10px, 0);
    }

JavaScript 引擎线程

JS引擎线程负责解析Javascript脚本,运行代码 JS引擎一直等待着任务队列中任务的到来,然后加以处理,一个Tab页(renderer进程)中无论什么时候都只有一个JS线程在运行JS程序

GUI渲染线程与JS引擎线程是互斥的,所以如果JS执行的时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞

浏览器定时触发器线程

浏览器定时计数器并不是由 JavaScript 引擎计数的, 因为 JavaScript 引擎是单线程的, 如果处于阻塞线程状态就会影响记计时的准确, 因此通过单独线程来计时并触发定时是更为合理的方案

浏览器事件触发线程

当一个事件被触发时该线程会把事件添加到待处理队列的队尾,等待 JavaScript 引擎的处理。这些事件可以是当前执行的代码块如定时任务、也可来自浏览器内核的其他线程如鼠标点击、AJAX 异步请求等,但由于 JavaScript 的单线程关系所有这些事件都得排队等待 JavaScript 引擎处理。

浏览器 http 异步请求线程

在 XMLHttpRequest 在连接后是通过浏览器新开一个线程请求, 将检测到状态变更时,如果设置有回调函数,异步线程就产生状态变更事件放到 JavaScript 引擎的处理队列中等待处理。