JS进阶(五)同步异步编程及浏览器的底层渲染机制

149 阅读5分钟

浏览器渲染机制

浏览器底层渲染机制

一个页面从服务器访问,拿到页面源代码之后做的事情是什么?

  1. 生成Dom树(DOM Tree) => 对HTML文件的处理

    • 基于HTML获取的是流文件 (进制编码)

    • 把进制编码编译为具体的字符

    • 按照令牌TOKEN进行解析 (分词/断词)

    • 生成具体的节点 (元素标签/文本节点....)

    • 按照相互的依赖关系生成一个DOM树 (节点树)

      在这里插入图片描述

  2. 生成CSSOM Tree => 对CSS文件进行处理

    在这里插入图片描述

  3. 生成渲染树(Render Tree)

    • DOM TREE + CSSOM TREE
    • Meat、Head、Link和display:none;等这些元素是不会出现在渲染树的
  4. 布局/回流/重排 Layout

    • 按照渲染树计算出每一个元素在视口中的位置和大小
  5. 分层

    • 按照计算出来的层级进行分层
      • 定位
      • 设置透明度 rgba
      • 设置滤镜
      • 文本超过盒子大小,裁切
    • 单独计算每一层的绘制列表(具体绘制)
  6. 绘制/重绘 Painting

    • 把生成的绘制列表提交给"合成线程"
    • "合成线程"进行我们最后的绘制,呈现在浏览器的页面上
  7. 光栅化,合成

分层之前的操作都是基于GUI渲染线程来完成

进程与线程

  • 进程: 一个程序(浏览器新建一个也卡就是一个进程) 【工厂】

  • 线程: 一个进程中可能包含多个线程,每个线程可以同时做一件事情,真正的同时做一件事情,必须依赖多线程【工人】

JS中的同步异步

浏览器就是多线程 , 但是JS本身是单线程。因为浏览器本身只分配了一个线程“GUI渲染线程”运行JS代码,JS本身从本质上来讲,是不能同时做多件事的。

  • 同步:上一件事情完成,再去做下一件事情
  • 异步: 上一件事情没有完成(我们把它做一些特殊处理),下一件事情继续执行 【绝对不是同时做两件事】

浏览器生成DOM TREE/CSSOM TREE ... 的过程也是单线程(配合浏览器的多线程去完成一些事情,例如资源请求就是利用浏览器的网络线程去做的)

浏览器具体的解析过程 "GUI渲染线程"

  1. 自上而下解析完所有的HTML标签/DOM节点后,DOM TREE就生成了

  2. 解析过程中会遇到比较特殊的

    • 外链式 link href = “”

      => 浏览器会分配一个新的HTTP网络进程去加载资源文件

      => 不会阻碍DOM树的渲染

    • 内嵌式 < style > 。。。</ style >

      => 不用去请求新的资源文件了,但是此时样式没有处理,浏览器会做一个记录 ,它会等所有的CSS资源加载回来之后,按照先后顺序依次渲染CSS,从而生成CSSOM树

    • @import 'xxx.css' 导入式

      => 虽然也是分配HTTP网络线程去加载文件,但是此时GUI渲染线程会被阻塞掉【阻碍DOM树的渲染】(只有等待资源加载回来,才会继续渲染DOM)

  3. 遇到script标签

    • 遇到内嵌js代码,会立即执行JS (阻碍dom树渲染)

    • 遇到script 外链js代码的

      • 阻碍DOM TREE的渲染,同时分配一个HTTP线程去加载资源文件,加载回来后立即执行JS ( JS 中没有采用异步,直接获取DOM元素,而DOM元素此时没有渲染,JS是获取不到的)

      • 把script放到页面底部 (先渲染DOM TREE,再执行JS,也可以获取到DOM元素了)

        想要script放在前面还可以获取dom,有3种解决办法
        > setTimeout(()=>{ ... }, 0) 
        > window.addEventListener('DOMContentLoaded',function(){ ...   })  触发条件:DOM TREE加载完即可
        > window.onload = function(){ ... }  => 触发条件是:所以dom资源都加载完成(包含DOM TREE/CSS/图片),再执行
        
      • async / defer 给script设置的属性

        > async 是开辟HTTP线程加载资源文件,此时DOM TREE继续,但是资源文件一旦加载回来,停止DOM TREE,先执行JS代码(不考虑JS引用顺序,谁先加载回来谁先执行)
        > defer 也是开辟HTTP线程加载资源文件,即使资源文件加载回来,也会等待DOM树渲染完成,defer效率更好,但是不兼容低版本浏览器
        

        在这里插入图片描述

    • 遇到img,正常情况下,老版本浏览器会阻塞dom渲染,新版浏览器虽然不会阻碍DOM渲染,但是会占用图片资源请求,会占用HTTP线程(浏览器只能同时开6-7个HTTP线程,这样的话,图片或者音视频资源加载本来就会慢一些,会影响关键(其他link/script)资源的加载), 图片资源的渲染也是比一般资源耗时间的,也会拖累渲染速度

以上性能优化的点:

1、不用@import

2、link标签放到HEAD中,尽可能提前加载资源文件,这样等DOM树渲染完,资源就加载回来了: 当代浏览器的机制越发完善,chrome的预加载扫描器通过"src","link"等属性,找到外部链接资源后预加载,避免了资源加载等待,同事实现了提前加载和执行分离

3、如果CSS代码比较少,尽可能使用内嵌式,可以减少HTTP请求,但是如果样式比较多,采用内嵌式,第一次加载HTML都会浪费很长时间,这样还不如基于link分开加载; 移动端开发都是内嵌优先(也要考虑CSS代码量)

4、尽可能减少DOM或者减少DOM的层级嵌套,以及标签语义化(当代前端开发,只把首屏结构/内容写出来,渲染知识首屏的,当首屏加载完,页面滚动的时候,在给予JS创建其他屏幕的结构和内容 => 骨架屏/SSR => 客户端股价屏,开始首屏结构都没有,只有一个loading或者展位图)

5、图片合并(sripte) / BASE64 / iconfont / svg / http2 升级协议

BASE64 不发松请求(好用但是要慎用,加大了文件体积)

6、图片懒加载 / 预加载

网络层优化

CRP浏览器关键节点优化 (critical rendering path)

webpack层优化

安全优化

代码层优化 闭包