关于浏览器底层渲染机制----理解

266 阅读7分钟

浏览器的底层渲染机制

进程:一个程序,浏览器打开一个页面就开启一个进程

线程:程序中具体“干活的”,浏览器具备很多线程,这样就可以同时做很多事 一个进程包含 1 到多个线程(进程大)

  • 同步编程”: 每一个同时只能做一件事,这件事处理完成才能处理下一件事情 -->单线程,同时只能处理一件事情
  • 异步编程” :多线程,同时可以处理多件事情
浏览器具备的线程:
  •       + GUI渲染线程:自上而下渲染页面的:含html,css,img
    
  •       + JS引擎线程:渲染和解析JS代码
    
  •       + HTTP网络请求线程:从服务器获取内容(最多同时可以开辟5~7个)
    
  •       + 定时器监听线程:监听定时器是否到达时间
    
  •       + 事件监听线程:监听事件是否触发
    
  •       + ...
    
当我们从服务器获取HTML代码后,浏览器需要按照代码规则,绘制出对应的页面
  1.     @1 创建“DOM TREE”;GUI渲染线程,自上而下一行行去渲染解析代码;当渲染到底部的时候,浏览器已经规划出:当前页面的结构和层级嵌套关系(节点和节点之间的关系),这就是DOM树
    
  2.     @2 创建“CSSOM TREE”;在DOM TREE 生成之后,浏览器把从服务器获取的样式代码渲染,生成“CSSOM TREE”;在GUI渲染过程中,可能会遇到link、style、img、script等标签,遇到不同标签会有不同处理方案
    
  3.     @3 把DOM TREE 和 CSSOM TREE合并在一起,形成 RENDER TREE
    
  4.     @4 Layout布局:根据视口大小,计算出每一个节点,在视口中的具体位置及大小等
    
  5.     @5 分层:规划出对应的层级,以及节点该在哪个层级上
    
  6.     @6 painting绘制:按照所有解析的规则,一层层的绘制
    
回流(Reflow)或重排:

页面第一次渲染完之后,后期基于某些操作,修改了某个节点在视口中的位置或大小,这样浏览器需要重新计算当前视口(当前层级中)所有节点的布局位置,也就是把 Layout操作重新来一遍,计算完层级,重新painting,我们把这个操作叫 “回流(Reflow)或重排”。回流非常消耗性能,我们常说的操作DOM消耗性能,指的就是这件事。

引发回流:
        +改变节点位置、大小
        +新增或引出节点
        +视口大小改变
        +内容改变引发节点的大小改变...
    而且回流一定引发重绘,页面第一次渲染就会有一次Layout和painting
  某些DOM操作(例如改变文字颜色,背景)并不会对节点的位置产生影响,此时仅需 “重绘(Repaint)”
在页面运行时的性能优化
         @1 读写分离:把设置元素样式代码和获取元素样式代码分离编写,不要穿插混合一起
           渲染队列机制:当代浏览器的机制,当前上下文代码执行过程当中,遇到修改元素样式代码,浏览器并没有立即处理,而是先存放在渲染队列中,当遇到获取元素样式操作(或当前上下文代码执行完毕),才会刷新渲染队列(即把队列中对DOM操作统一处理),只引发一次回流
         @2 统一修改样式
           + 把需要修改的样式统一写到css样式表中,基于修改元素样式类名达到修改样式的需求,只会引发一次回流
             box.classList.add('XXX');
           + 基于cssText处理
             box.style.cssText='width:100px;height:200px';
         @3 新增DOM元素采用批量新增
           + 文档碎片:一个临时存储DOM节点容器(可以设置一个变量承接)
           + 模板字符串拼接
         @4 修改元素的样式尽可能使用 transform 变形属性/opacity ,它的改变不会引发DOM回流,浏览器内部对其做了硬件加速,也可以理解为这就是“规定”
         @5 修改样式的元素尽可能在单独的文档流中,虽然不能减少DOM回流,但是可以处理的更快,渲染绘制的时候只针对当前文档流
         @6 JS处理动画的规则:
            + 能用css3动画解决的,坚决不用JS动画
            + 适当情况下,牺牲平滑度来换取速度

       不要直接操作DOM,使用vueReact框架,我们只操作数据,操作DOM交给框架去做
  ** 前端性能优化核心操作:减少操作DOM产生的回流、重排、**
  
  
加快页面第一次渲染,减少白屏等待时间
   关于样式做法:
     + 遇到内嵌式的<style>: 无需去服务器获取样式代码,但也不会立即渲染,要等到DOM树生成完,外链式获取的样式代码都拿到了,再按照编写的先后顺序依次渲染解析样式代码,以此保证css优先级正确
     + 遇到外链式的<link>: 单独开辟一个新的HTTP线程去服务器获取样式代码,而GUI渲染线程会继续向下渲染,属于‘异步操作’;也就是CSS样式代码渲染,一般发生在DOM树生成之后
     + 遇到导入式@import: 也会开辟一个HTTP线程,从服务器获取样式代码,只不过它会阻碍GUI渲染,也就是,样式代码没有请求回来之前,GUI暂停渲染,‘同步操作’、
     + 遇到<img>:
        + 开辟一个新的HTTP线程请求图片,GUI继续渲染,“异步编程”
        + 当获取到图片资源后,浏览器首先进行编码,然后按照编码进行渲染绘制
     + 遇到<script>
       + 开辟新的HTTP线程去获取代码,同时阻碍GUI渲染,  “同步编程”
     
优化方案
       @1 当样式代码较少的情况下,直接使用内嵌式放在HTML页面即可,没必要从服务器获取,这样可以在DOM TREE生成后立即渲染样式,生成CSSOM TREE,加快页面渲染速度(移动端经典优化方案)
       @2 样式代码较多,基于<link>外链式
         + 最好把CSS都写入到一个样式表中,只请求一次即可,减少HTTP请求次数
         + 把<link>放在HEAD中,这样保证样式资源的提前获取,当DOM  TREE生成后可能样式代码已经回来了
       @3 非必要情况,不用@import,它会阻碍GUI渲染
    
遇到img优化方案
        @1 虽然图片不会阻碍GUI渲染,但是每次请求都会占用一个HTTP线程,而浏览器可同时开启的线程是有限的,会导致其他类型资源获取延后,所以需要做图片的“懒加载”
        @2 如果想加快图片的渲染,我们可以跳过 获取资源&编码 这两步,直接让浏览器 绘制
          + 如果图片较大,生成的BASE64码会很多,这样会增加CSS文件的体积,而且代码维护麻烦,所以不能滥用
          + 小图片可以BASE64,大图只有在各种解决办法都试过之后,发现还是达不到自己的要求,此时尝试使用BASE64,会发现此时图片渲染速度会明显提高(后期可以通过webpack可以自动打包BASE64)
    
遇到script优化方案
       @1 把<script>放在页面底部
       @2 把<script>获取资源改为“异步编程”,在标签上加 async/defer 属性
          + <script async>:开辟新的HTTP,去获取JS代码;获取过程中,GUI继续渲染,获取到了之后立即暂停GUI,先把js继续执行,再执行GUI; “半异步执行”
          + <script defer>:和link相似,开辟新的HTTP,‘异步’去获取JS代码,GUI继续渲染,哪怕js获取到,也会先把GUI渲染完成,再渲染解析JS; “异步执行”
       
          如果没有和JS之间的相互依赖,使用async即可;但如果需要按照编写的先后顺序去执行,就使用defer
          
       @3 和CSS文件一样,都合并到一个文件中,减少HTTP请求次数