# 1、CSS资源的加载(异步加载的)

  • 遇到 style 内联样式,"同步"交给GUI 渲染线程解析。
  • 遇到 link 标签。
    • "异步" 开辟一个新的 HTTP网络请求线程。
    • 不等待资源信息请求回来,GUI 渲染线程继续向下渲染。
    • GUI 渲染线程同步操作都处理完后,再把基于HTTP 网络线程请求回来的资源文件进行解析渲染。
  • 遇到 @import 导入样式。同步开辟一个新的 HTTP网络请求线程 去请求资源文件。在资源文件没有请求回来之前,GUI 渲染线程会被"阻塞",不允许其继续向下渲染。

# 2、script 资源的加载(默认是同步加载的)

  • 默认是"同步"的:必须基于HTTP 网络线程,把资源请求回来之后,并且交给js 渲染线程解析完成后,GUI 渲染线程才能继续向下渲染。script默认是阻碍GUI渲染的。
  • async属性:遇到 <script async>时,首先也是开辟一个HTTP网络线程去请求加载资源文件,与此同时GUI渲染线程继续向下渲染(吧默认的同步改为"异步"),但是一旦资源请求回来后,会中断GUI的渲染,先把请求回来的js进行渲染解析。才会渲染解析请求回来的js 代码。

# 3、图片或者音频、视频资源

  会发起新的HTTP网络请求,请求加载的资源文件,不会阻碍GUI的渲染("异步")。当GUI 渲染完成后,才会把请求回来的资源信息进行渲染解析。

# 4、页面渲染的步骤

  • 生成DOM TREE(DOM树):自上而下渲染页面,整理好整个页面的DOM结构关系。
  • 生成CSSOM TREE(样式树):当把所有的样式资源请求加载回来后,按照引入CSS的顺序,依次渲染样式代码,生成样式树。
  • RENDER TREE(渲染树):将DOM TREECSSOM TREE 合成渲染树(displaynone的不会去渲染)。
  • Layout 布局/回流/重绘:计算它们在设备视口(viewport)内的确切位置和大小。
  • 分层处理:按照层级定位分层处理,每一个层级都会详细规划处具体的绘制步骤。
  • Painting:按照每一个层级计算绘制的绘制步骤,开始绘制页面。

# 5、前端性能优化(CRP:

  • 生成 DOM TREE阶段
    • 减少dom层级嵌套
    • 不要使用非标准标签。
  • 生成 CSSOM
    • 尽可能不要使用@import(阻塞GUI渲染)
    • 如果CSS代码较少,尽可能使用style内联样式
    • 如果使用 link,尽可能把所有的样式资源合并成一个 CSS,且压缩(减少HTTP 请求数量,同时在渲染 CSS的时候,也不需要在计算依赖关系)
    • CSS选择器链短一些(CSS选择器渲染时从右到左的)
    • link等导入CSS的操作放在HEAD中。
  • script资源的优化
    • script标签。尽量放在页面的底部。
    • 使用async时,哪一个资源先获取到,就把这个资源代码渲染执行。
    • 使用defer时,和link一样,等所有<script defer>都请求回来后,按照导入顺序/依赖关系依次先后渲染。
  • img资源的优化
    • 懒加载:第一次加载页面的时候不要加载请求图片,哪怕是异步的,也会占据HTTP并发的数量,导致其他资源后加载。
    • 图片的BASE64:不用去请求图片,BASE64码基本上代表的就是图片,而且页面渲染图片的时候速度很快(但是要慎用(编码后的代码量太大),在webpack工程化中可以使用,因为它基于file-loader,可以自动base64)
  • Layout/Painting:减少DOM的"回流/重排"和重绘。 触发回流,必然会触发重绘;单纯的重绘,并不会引发回流。
    • 引发回流的操作:

      • 元素在视口中的大小或者位置发生变化
      • 元素的删减或者新增(或者基于display控制显示隐藏)。
      • 浏览器视口发生变化。
      • 内容发生变化(比如文本变化或者图片被另一个不同尺寸的图片所替代)
    • 避免DOM的回流的操作:

      • 样式集中改变
      • 分离读写操作
      • 缓存布局信息
      //触发两次回流 两次样式读
      box.style.width=box.offsetWidth+10+"px";
      box.style.height=box.offsetHeight+10+"px";
      //修改为
      const w=box.offsetWidth,h=box.offsetHeight;
      box.style.width=w+10+'px';
      box.style.height=h+10+'px';
      
      1
      2
      3
      4
      5
      6
      7
      • 元素批量修改(createDocumentFragment,模板字符串拼接)
      • 动画效果等频繁样式修改的操作,应用到position属性为absolute或者fixed的元素上(脱离文档流,单独一层)。利用分层机制,如果只改变一个层面上的位置大小等消息,浏览器回流和重绘的速度会加快很多。
      • CSS3硬件加速(GPU加速)。transform、opacity、filters...等属性会触发硬件加速,不会引发回流。但是过量使用会占用大量内存、性能消耗严重,有时候会导致字体模糊等。
      • 避免table布局和使用cssjavascript表达式。
    • 重绘:元素的颜色、透明度等不影响元素的大小及在视口中的位置的操作。(outline、visibility、background-color)

# 6、当代浏览器的渲染队列机制

  在当前上下文操作当中,遇到一行修改样式的代码,并没有立即通知浏览器渲染,而是把其放置在渲染队列中,接下来看是否还有修改样式的代码,如果有继续放在渲染队列中...一直到再也没有修改样式的代码或者"遇到一行获取样式的操作",这样都会刷新浏览器的渲染队列机制(也就是把现在队列中修改样式的操作,统一告诉浏览器渲染,这样只会引发一次回流。)。优化方式就是**分离读写。**或者样式集中修改box.cssText="...."

  • 分离读写的方式1
//分离样式读和写
//这种写法这会引发两次回流
box.style.width="100px";//样式写
box.style.height='200px';//样式写
box.offsetHeight;//样式读
box.style.top="100px"; //样式写
//改为
box.style.width="100px";//样式写
box.style.height='200px';//样式写
box.style.top="100px";//样式写
box.offsetHeight;//样式读
1
2
3
4
5
6
7
8
9
10
11
  • 分离读写的方式2 box.style.cssText="width:100px;height:200px" 注意:有些操作可以利用样式的回流来实现一些想要的操作。
setTimeout(() => {
    // 立即回到left:0的位置
    box.style.transitionDuration = '0s';
    box.style.left = 0;
    // 刷新渲染队列(会增加一次回流)
    box.offsetLeft;
    // 回到开始位置后,再次运动到left:200位置(有动画)
    box.style.transitionDuration = '0.5s';
    box.style.left = '200px';
}, 1000);
1
2
3
4
5
6
7
8
9
10
Last Updated: 6/11/2024, 11:35:27 AM