DOM的渲染将HTML,CSS,JS等资源后,解析-构建树-绘制,最后呈现给用户能看到的界面的这个过程。
页面的渲染,JS的执行,事件的触发都是在渲染进程中进行的。
渲染进程包含多个线程
为什么js是单线程的,假如JS是多线程的,假设现在有2条线程,一条在dom节点上添加节点,另一条删除这个节点。
GUI 渲染线程与 JS 引擎线程是互斥的,当 JS 引擎执行时 GUI 线程会被挂起,GUI 更新会被保存在一个队列中等到 JS 引擎空闲时立即被执行。
渲染进程的创建时机
每次新开一个标签页,都会创建一个新的渲染进程。
但有例外,比如从A页面里面打开一个新的页面B页面,而A页面和B页面又属于同一站点的话,A和B就共用一个渲染进程。
![]()
标签加载图片
DOM树的结构可以通过document打印查看,DOM结构,DOM和HTML内容几乎是一样的,但是和HTML不同的是,DOM是保存在内存中树状结构,可以通过JavaScript来查询或修改其内容。
这个过程中,display:none的元素、script标签、注释也都会添加到DOM树中。
渲染树中只包含渲染可见的节点
从DOM树的根节点开始向下遍历每个子节点,忽略所有不可见的节点,比如display:none、head标签。在CSSOM中为每个可见的子节点找到对应的规则并应用。
第一次确定节点大小和位置称为布局,之后重新触发页面布局可以称为回流
遍历布局树进行分层,生成分层树后,为每个图层分别进行绘制,在绘制中不同的图层渲染互不影响。

如果一个节点没有对应的层,那么这个节点就从属于父节点的图层。
渲染引擎为特点的节点创建新的层
拥有层叠上下文属性的元素会被提升为单独一层

比如非static的position、z-index、filter、opacity等
需要剪裁(clip)的地方也会被创建为图层。
这个阶段可以开启GPU加速

所以图片加载不会阻塞DOM渲染
概念
DOM解析 :浏览器向服务器请求到了 HTML 文档后便开始解析,产物是 DOM(文档对象模型),到这里 HTML 文档就被加载和解析完成了。 DOM的解析就是生成DOM树的过程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的解析
补充知识1:浏览器解析DOM时,虽然会一行一行向下解析,但是它会预先同时加载具有引用标记的外部资源(例如带有src标记的
标签),而在解析到此标签时,则无需再去加载,直接运行,以此提高运行效率。
补充知识2:浏览器无法预先知道脚本的具体内容,因此在碰到标签时,会触发页面渲染,确保脚本内能获取到DOM的最新的样式。
defer、async 仅仅是改变脚本的执行时机
JS三种异步加载的方式
| 方式 | 执行时机 | 执行顺序 |
|---|---|---|
type="module" | 浏览器加载 ES6 模块,整个页面解析完毕,再执行模块脚本。等同于defer属性 | 使用相同的执行队列,谁在前面谁先执行。 |
script标签中defer属性 | 页面解析完毕再执行 | 如果有多个defer脚本,会按照它们在页面出现的顺序加载。放入队列中,先进先出。 |
script标签中async属性 | 脚本下载完,渲染线程就会中断渲染,执行这个脚本以后,再继续渲染 | 而多个async脚本是不能保证加载顺序的 |
| script 标签 | JS 执行顺序 | 是否阻塞解析 HTML |
|---|---|---|
| 在 HTML 中的顺序 | 阻塞 | |
| 网络请求返回顺序 | 可能阻塞,也可能不阻塞 | |
| 在 HTML 中的顺序 | 不阻塞 |
渲染进程在解析 HTML 的时候,如果遇到一个没有任何属性的 script 标签,就会暂停解析,先发送网络请求获取该 JS 脚本的代码内容,然后让 JS 引擎执行该代码,当代码执行完成后再切换回渲染引擎继续渲染流程。

async、defer属性的js脚本,渲染引擎遇到这一行命令,就会开始下载脚本,同时直接执行后面的命令。
async脚本会在JS下载完毕后立即执行
假设此时HTML没有解析完,会暂停渲染,先让JS引擎执行代码,执行完毕后再切换回渲染引擎继续渲染流程。

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

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

对于浏览器说,页面加载主要有两个事件
从重绘和回流方面进行优化
style,采用修改class的方式从其他渲染阶段
window.requestAnimationFrame(回调)