• DOM渲染与优化 - CSS、JS、DOM解析和渲染阻塞问题


    DOM渲染

    面试题

    • js的DOM渲染是单线程的,那渲染的过程是什么样的?浏览器渲染页面的过程?
    • script标签,包含async属性的script标签,包含defer属性的script标签对文档渲染有啥影响?
    • css是否阻塞页面的解析和渲染?css渲染会不会阻塞dom渲染,会不会阻塞dom树建立
    • js会阻塞dom渲染吗,图片加载会阻塞dom渲染吗?
    • Dom渲染是在事件循环机制哪里实现的
    • JS加载阻塞DOM渲染问题,怎么解决
    • 生成DOM树和CSSOM树之后怎么生成渲染树

    DOM的渲染过程

    DOM渲染的时机与渲染进程的概述

    DOM的渲染将HTML,CSS,JS等资源后,解析-构建树-绘制,最后呈现给用户能看到的界面的这个过程。
    页面的渲染,JS的执行,事件的触发都是在渲染进程中进行的。

    渲染进程包含多个线程

    • JS引擎线程:JavaScript引擎V8,负责处理JavaScript脚本程序。

    为什么js是单线程的,假如JS是多线程的,假设现在有2条线程,一条在dom节点上添加节点,另一条删除这个节点。

    • GUI 渲染线程: 负责渲染浏览器界面,解析 HTML,CSS,构建render树,布局和绘制等。

    GUI 渲染线程与 JS 引擎线程是互斥的,当 JS 引擎执行时 GUI 线程会被挂起,GUI 更新会被保存在一个队列中等到 JS 引擎空闲时立即被执行。

    • 事件触发线程: 控制事件循环,当对应的事件符合条件被触发时,该线程会将事件添加到待处理的事件队列中,等待JS引擎的处理。
    • 定时器触发线程:当使用setTimeout或者setInterval时,需要定时器线程计时。计时完成后会将特定的事件推进事件触发线程的任务队列中,等待进入主线程执行。
    • 异步http请求线程:XMLHttpRequest在连通后通过浏览器新起一个线程请求。检测到状态变化时,如果有设置回调函数,异步线程就产生状态变更事件,将这个回调再放入事件队列中,再由JS引擎执行。

    渲染进程的创建时机
    每次新开一个标签页,都会创建一个新的渲染进程。
    但有例外,比如从A页面里面打开一个新的页面B页面,而A页面和B页面又属于同一站点的话,A和B就共用一个渲染进程。

    浏览器的渲染流程

    1. 解析HTML生成DOM树:遇到标签加载图片

    在这里插入图片描述DOM树的结构可以通过document打印查看,DOM结构,DOM和HTML内容几乎是一样的,但是和HTML不同的是,DOM是保存在内存中树状结构,可以通过JavaScript来查询或修改其内容。
    这个过程中,display:none的元素、script标签、注释也都会添加到DOM树中。

    2. 解析CSS生成CSSOM(CSS Object Model): 遇见背景图片链接不加载
    3. 将DOM树和CSSOM树合并生成渲染树:加载可视节点的背景图片

    渲染树中只包含渲染可见的节点
    从DOM树的根节点开始向下遍历每个子节点,忽略所有不可见的节点,比如display:nonehead标签。在CSSOM中为每个可见的子节点找到对应的规则并应用。

    4. 遍历渲染树开始布局,计算每个节点的位置大小等信息,输出包含DOM元素样式和位置的布局树。

    第一次确定节点大小和位置称为布局,之后重新触发页面布局可以称为回流

    5. 绘制:开始渲染图片

    遍历布局树进行分层,生成分层树后,为每个图层分别进行绘制,在绘制中不同的图层渲染互不影响。

    在这里插入图片描述
    如果一个节点没有对应的层,那么这个节点就从属于父节点的图层。

    渲染引擎为特点的节点创建新的层

    1. 拥有层叠上下文属性的元素会被提升为单独一层
      2. 在这里插入图片描述
      比如非static的positionz-indexfilteropacity

    2. 需要剪裁(clip)的地方也会被创建为图层。

    6. 合成: 已绘制的不同图层合并成在一起,输出到屏幕。

    这个阶段可以开启GPU加速
    在这里插入图片描述
    所以图片加载不会阻塞DOM渲染

    CSS、JS、DOM解析和渲染阻塞问题

    概念

    1. DOM解析 :浏览器向服务器请求到了 HTML 文档后便开始解析,产物是 DOM(文档对象模型),到这里 HTML 文档就被加载和解析完成了。 DOM的解析就是生成DOM树的过程
    2. DOM渲染:浏览器是解析DOM生成DOM Tree,结合CSS生成的CSS Tree,最终组成render tree,再渲染页面的过程。

    结论

    • async/defer的JS执行(同步的JS执行)会阻塞DOM的解析过程
      GUI 渲染线程与 JavaScript 引擎为互斥,当 JS 引擎执行时 GUI 线程会被挂起。直到 JS 程序一轮调度执行完成,才会接着执行。因此如果 JS 执行的时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞的感觉。

    • CSS不会阻塞DOM的解析,但会阻塞DOM的渲染

    DOM解析和CSS解析是两个并行的进程,所以CSS本身不会阻塞DOM的解析
    通过JS间接影响 CSS加载会阻塞后面的JS语句执行 --> JS语句阻塞DOM的解析

    • CSS加载会阻塞后面的JS语句执行
      GUI 渲染线程与 JavaScript 引擎为互斥

    补充知识1:浏览器解析DOM时,虽然会一行一行向下解析,但是它会预先同时加载具有引用标记的外部资源(例如带有src标记的