JavaScript高级手记(浏览器底层渲染机制和性能优化)

158 阅读5分钟

JavaScript高级手记(浏览器底层渲染机制和性能优化)

/* 
 * 客户端从服务器获取到需要渲染的页面的源代码后
      【开辟一个GUI渲染线程,自上而下解析代码,最后绘制出对应的页面】

 * 自上而下解析代码是同步的,但是某些操作是异步的
     请求加载属于资源的获取
     渲染解析属于代码执行

 *   1、关于CSS资源的加载
 *      + 遇到的是<style> 内嵌样式
 *         => 同步:交给GUI渲染线程去解析
 *      + 遇到的是<link> 外联样式
 *        => 异步:会开辟一个新的HTTP网络请求线程去获取文件(遇到一个link开辟一个新的http网络线程,但是同一浏览器同一个页面最多开辟4~6个)
 *        => GUI渲染线程不会等待,而是继续往下解析渲染代码
 *        => GUI渲染线程在同步任务都处理完成之后,再把基于HTTP网络线程请求回来的资源文件进行解析渲染
 *     + 遇到的是@import 导入式样式
 *       => 同步:开辟一个新的HTTP网络线程请求资源
 *       => 但是在资源文件没有请求回来之前,GUI渲染线程会被“阻塞”,不允许其往下渲染
 * 
 *  2、遇到<script>资源的请求
 *     + 默认都是同步的:必须基于HTTP网络线程把资源文件请求回来之后,并且交给“JS解析线程”解析渲染完成后,GUI渲染线程才能继续向下渲染,所以<script>默认也是“阻碍GUI渲染”的
 *     + async属性:遇到<script async>
 *       => 异步:首先也是开辟一个HTTP网络线程去请求资源文件
 *       => GUI渲染线程不等,继续往下渲染解析
 *       => 但是一旦请求资源回来后,会中断GUI渲染,先把请求回来的JS文件解析执行完成之后,GUI渲染线程才会继续往下渲染解析
 *     + defer属性:遇到<script defer>
 *      => 异步:新开辟一个HTTP网络线程去请求资源
 *      => GUI渲染线程不等,继续往下渲染
 *      => GUI渲染完成之后,才会把请求回来的JS文件解析执行
 * 
 *  3、遇到<img>或者<video>资源的请求
 *     => 异步:也会发送新的HTTP网络线程去请求加载对应的资源文件
 *     => GUI渲染线程不会被“阻塞”,而是继续向下渲染解析
 *     => 直到GUI渲染完成后,才会把请求回来的资源文件进行渲染解析
*/
/* 
 * 异步:
      + <link>
      + <script async>
      + <script defer>
      + <img>
      + <video>
 * 同步:
      除了异步的,都是同步的
*/


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


 * 前端性能优化  [CRP:关键渲染路径]
      + 生成DOM TREE
          + 减少DOM的层级嵌套
          + 不要使用“非标准”的标签

      + 生成CSSOM Tree
         + 尽可能不使用@import(阻塞GUI渲染)
         + 如果CSS代码比较少,尽可能使用内嵌样式(尤其是移动端开发)
         + 如果使用link,尽可能把所有的样式资源合并为一个css文件,并且进行压缩(减少http请求数量,以及渲染CSSOM树的时候,也不需要再计算其引入顺序)
         + CSS选择器链短一些(因为CSS选择器渲染是从右到左的)
         + 把link等导入CSS的操作放在head中(目的是:一开始加载页面就开始请求资源,同时GUI去生成DOM树 “CSS等资源预先加载”)
    
     + 对于其他资源的优化
        + 对于<script>:尽可能放置在页面的底部(防止其阻塞GUI的渲染);对于部分<script>需要使用async或者defer
          + async是不管JS的依赖关系的,哪一个资源先请求到,就先把这个资源代码渲染执行
          + defer和link一样:等所有的<script defer>都请求回来后,按照导入顺序/依赖关系再依次渲染执行的

        + 对于<img>
          + 懒加载:第一次加载页面的时候不要加载请求图片,哪怕它是异步的,但是也占据了HTTP并发的数量,导致其他资源延后加载
          + 图片的 base64:不用去请求加载图片,base64码基本上代表的就是图片,而且页面渲染base64码的速度很快(慎用:因为它太长了)
          + ...

    + Layout/Painting:重要的优化手段(减少DOM的回流和重绘)
       + 第一次加载页面必然会有一个回流和重绘
       + 触发回流,则一定也会触发重绘;如果只是单纯的重绘,则不会引发回流;性能优化点都在回流上

*/

// 操作DOM会消耗性能? =>DOM回流导致的

/* 
 * 触发回流条件:
     + 元素在视口中的大小或者位置发生变化
     + 元素的删除或者新增,以及基于display:none控制隐藏
     + 浏览器视口大小发生改变
     + ...
   
  这些操作都会导致让浏览器重新计算每一个DOM元素在视口中的位置和大小(也就是Layout/Reflow)
*/

/* 
 * 当代浏览器的渲染队列机制:
      在当前上下文操作中,遇到一行修改样式的代码,并没有立即通知浏览器进行渲染,而是会把其放置在渲染队列中,接着往下看是否还有修改样式的代码,如果有,则继续放置在渲染队列中...一直到再也没有修改样式的代码
*/
box.style.height = '20px';
box.style.width = '100px';
box.style.position = 'absolute';
box.style.top = '100px';
// 上面四行修改样式的代码最后只会触发一次回流