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

文章目录

  • DOM渲染
    • 面试题
    • DOM的渲染过程
      • DOM渲染的时机与渲染进程的概述
      • 浏览器的渲染流程
        • [1. 解析HTML生成DOM树:遇到`<img>`标签加载图片](#1. 解析HTML生成DOM树:遇到<img>标签加载图片)
        • [2. 解析CSS生成CSSOM(CSS Object Model): 遇见背景图片链接不加载](#2. 解析CSS生成CSSOM(CSS Object Model): 遇见背景图片链接不加载)
        • [3. 将DOM树和CSSOM树合并生成渲染树:加载可视节点的背景图片](#3. 将DOM树和CSSOM树合并生成渲染树:加载可视节点的背景图片)
        • [4. 遍历渲染树开始布局,计算每个节点的位置大小等信息,输出包含DOM元素样式和位置的布局树。](#4. 遍历渲染树开始布局,计算每个节点的位置大小等信息,输出包含DOM元素样式和位置的布局树。)
        • [5. 绘制:开始渲染图片](#5. 绘制:开始渲染图片)
        • [6. 合成: 已绘制的不同图层合并成在一起,输出到屏幕。](#6. 合成: 已绘制的不同图层合并成在一起,输出到屏幕。)
      • CSS、JS、DOM解析和渲染阻塞问题
        • [JS加载阻塞DOM渲染问题,怎么解决? - 异步JS,JS三种异步加载的方式](#JS加载阻塞DOM渲染问题,怎么解决? - 异步JS,JS三种异步加载的方式)
          • [script 标签中的 async 和 defer 属性](#script 标签中的 async 和 defer 属性)
        • DOMContentLoaded和Load
    • 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树:遇到<img>标签加载图片

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. 拥有层叠上下文属性的元素会被提升为单独一层

    比如非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标记的<script>标签),而在解析到此标签时,则无需再去加载,直接运行,以此提高运行效率。

补充知识2:浏览器无法预先知道脚本的具体内容,因此在碰到<script>标签时,会触发页面渲染,确保<script>脚本内能获取到DOM的最新的样式。

JS加载阻塞DOM渲染问题,怎么解决? - 异步JS,JS三种异步加载的方式

defer、async 仅仅是改变脚本的执行时机

JS三种异步加载的方式

方式 执行时机 执行顺序
type="module" 浏览器加载 ES6 模块,整个页面解析完毕,再执行模块脚本。等同于defer属性 使用相同的执行队列,谁在前面谁先执行。
script标签中defer属性 页面解析完毕再执行 如果有多个defer脚本,会按照它们在页面出现的顺序加载。放入队列中,先进先出。
script标签中async属性 脚本下载完,渲染线程就会中断渲染,执行这个脚本以后,再继续渲染 而多个async脚本是不能保证加载顺序的
script 标签 JS 执行顺序 是否阻塞解析 HTML
<script> 在 HTML 中的顺序 阻塞
<script async> 网络请求返回顺序 可能阻塞,也可能不阻塞
<script defer> 在 HTML 中的顺序 不阻塞
script 标签中的 async 和 defer 属性

渲染进程在解析 HTML 的时候,如果遇到一个没有任何属性的 script 标签,就会暂停解析,先发送网络请求获取该 JS 脚本的代码内容,然后让 JS 引擎执行该代码,当代码执行完成后再切换回渲染引擎继续渲染流程。

async、defer属性的js脚本,渲染引擎遇到这一行命令,就会开始下载脚本,同时直接执行后面的命令。

async脚本会在JS下载完毕后立即执行

假设此时HTML没有解析完,会暂停渲染,先让JS引擎执行代码,执行完毕后再切换回渲染引擎继续渲染流程。

HTML 解析完了之后,async脚本才加载完,然后再执行脚本

defer会等HTML加载解析完再执行
defer的优先级高于DOMContentLoaded事件 。页面渲染完毕后先执行defer属性的脚本,再触发DOMContentLoaded事件。

DOMContentLoaded和Load

对于浏览器说,页面加载主要有两个事件

  • DOMContentLoaded: 就是当页面的内容解析完成后(页面DOM加载完成就触发),则触发该事件
  • Load:等待页面的所有资源都加载完成才会触发,这些资源包括css、js、图片视频

DOM渲染优化

从重绘和回流方面进行优化

  • 开启GPU加速
    • transform
    • opacity
    • will-change
  • JS优化
    • 对于 scroll 等事件进行防抖/节流处理。
    • 使用变量缓存对敏感属性值(offset等)的计算
    • 避免频繁改动使用style,采用修改class的方式

从其他渲染阶段

  • 使用Web Worker开启多线程,将纯计算的工作迁移到Web Worker上处理(防止js执行事件过程阻塞页面渲染)。
  • 使用CDN获取css等静态资源(加快css的加载速度)
  • 避免动画实现中丢帧造成卡顿现象,尽量不用计时器实现动画,而采用window.requestAnimationFrame(回调)
相关推荐
白兰地空瓶11 小时前
🚀 10 分钟吃透 CSS position 定位!从底层原理到避坑实战,搞定所有布局难题
前端·css
T___T11 小时前
Ajax 数据请求详解与实战
javascript·面试
onthewaying11 小时前
在Android平台上使用Three.js优雅的加载3D模型
android·前端·three.js
冴羽12 小时前
能让 GitHub 删除泄露的苹果源码还有 8000 多个相关仓库的 DMCA 是什么?
前端·javascript·react.js
悟能不能悟12 小时前
jsp怎么拿到url参数
java·前端·javascript
程序猿小蒜12 小时前
基于SpringBoot的企业资产管理系统开发与设计
java·前端·spring boot·后端·spring
Mapmost12 小时前
零代码+三维仿真!实现自然灾害的可视化模拟与精准预警
前端
程序猿_极客12 小时前
JavaScript 的 Web APIs 入门到实战全总结(day7):从数据处理到交互落地的全链路实战(附实战案例代码)
开发语言·前端·javascript·交互·web apis 入门到实战
suzumiyahr12 小时前
用awesome-digital-human-live2d创建属于自己的数字人
前端·人工智能·后端
萧曵 丶12 小时前
Python 字符串、列表、元组、字典、集合常用函数
开发语言·前端·python