回顾下基础知识~,记录学习笔记。
页面表现
关于DOMContentLoaded
事件的特点,先来一张对比图。
上面这张图是DOMContentLoaded
事件和load
事件的一个对比,可以看到,DOMContentLoaded
事件在页面进入后就触发了,而load
时间需要等到img图片完全加载后才触发。
DOMContentLoaded
DOMContentLoaded
按字面意思来说,DOM内容加载完成。它其实是Javascript
提供的一种用于监听DOM加载状态的机制。
使用代码如下:
document.addEventListener("DOMContentLoaded", (event) => {
console.log("DOM 完全加载和解析");
});
MDN描述:
当 HTML 文档完全解析,且所有延迟脚本(
<script defer src="…">
和<script type="module">
)下载和执行完毕后,会触发DOMContentLoaded
事件。它不会等待图片、子框架和异步脚本等其他内容完成加载。
从定义上来看,触发DOMContentLoaded事件只跟DOM加载和解析有关,只要DOM加载和解析完毕就会触发该事件,而影响DOM加载和解析的<script>
,也间接影响了触发DOMContentLoaded事件的时机,而css不会影响DOM的解析,css影响的是页面渲染。
用一句话总结就是:当HTML文档加载完毕,所有引用的内联js、以及外联js的同步代码都执行完成后触发DOMContentLoaded事件。
想要彻底弄懂HTML文档的加载和解析,还需要了解一些浏览器渲染相关的原理,详细的内容感兴趣的掘友可以自行检索相关知识点~
上面的图是从CaiBoBo大佬那里复制来的,拿这个图简单说下浏览器渲染步骤:
浏览器渲染流程
-
HMTL解析
-
输入:浏览器接收服务器返回的HMTL文档,将其下载下来。
-
处理:浏览器使用HTML解析器将HTML文档解析成DOM(文档对象模型)树。DOM树表示了文档的结构和内容。其实解析器的作用就是将一个元素通过一定的规则转换成另外一种形式。
-
输出:DOM树。
-
-
CSS解析与CSSOM树构建
-
输入:CSS样式表(可以是外部链接、内部样式或者内联样式)。
-
处理:CSS解析器解析CSS样式,构建CSSOM(CSS对象模型)树。CSSOM树包含了文档中所有元素的样式信息。
-
输出:CSSOM树。
-
-
渲染树构建
-
输入:DOM树和CSSOM树。
-
处理:浏览器将DOM树和CSSOM树合并,构建渲染树(Render Tree)。渲染树只包含需要显示的节点和它们的样式信息。
-
输出:渲染树。
-
-
布局(Reflow/Layout)
-
输入:渲染树。
-
处理:浏览器遍历渲染树的每个节点,计算每个元素的确切位置和大小(包括盒模型)。这个过程称为布局或重排(Reflow)。
-
输出:带有位置和大小的渲染树(更新后的)。
-
-
分层(Layering)
-
输入:带有位置和大小的渲染树。
-
处理:浏览器将渲染树中的元素按一定规则分成多个图层(Layer)。每个图层可以独立绘制,以提高渲染效率。
-
输出:分层后的渲染树。
-
-
绘制(Painting)
-
输入:分层后的渲染树。
-
处理:浏览器为每个图层生成绘制指令集,将每个图层的内容绘制到屏幕上。这个过程称为绘制或重绘(Repaint)。
-
输出:屏幕上的像素数据。
-
-
合成(Compositing)
-
输入:每个图层的绘制结果。
-
处理:合成线程将所有图层的绘制结果合并成一个最终的图像,显示在屏幕上。
-
输出:最终渲染的网页。
-
PS:上面这个流程是高度简化的,实际的浏览器渲染过程包含更多的细节和优化步骤,渲染过程中的每个阶段都可能受到JavaScript执行、样式变更、DOM操作等因素的影响,导致重排(Reflow)和重绘(Repaint)。
上面提到过如果HTML文档中含有<script>
标签,会阻塞HTML文档的解析,设置不同的<script>
标签对解析HTML文档也有很大的影响。
额外知识点
-
内联js
- 在HTML元素上直接使用Javascript代码作为属性值
- 在HTML元素内部使用
<script>
标签
-
外联js
- 将JavaScript代码编写在一个或多个独立的
.js
文件中,然后通过HTML文档中的<script>
标签将这些外部JavaScript文件引入到网页中
- 将JavaScript代码编写在一个或多个独立的
-
带有
async
属性的<script>
标签-
非阻塞加载:带有
async
属性的脚本加载是异步的,不会阻塞HTML文档的解析,浏览器可以继续向下解析和渲染。不过,当脚本加载完成后,会立即执行脚本内的代码,此时会阻塞页面渲染。 -
顺序加载:在带有async属性的脚本之间,尽管是异步加载的,但是它们之间会保持顺序执行。不过,对于没有
async
属性的脚本,或者<script>
标签内直接编写的脚本,它们仍然会按照在HTML文档中出现的顺序阻塞页面解析。
-
-
带有
defer
属性的<script>
标签-
延迟脚本执行:脚本的加载不会阻塞页面的解析和渲染过程,浏览器可以寄继续解析页面的其余部分,当整个文档完成解析后,在触发
DOMContentLoaded
事件之前执行这些脚本。 -
顺序加载:在带有defer属性的脚本之间,尽管是异步加载的,但是它们之间会保持顺序执行。
-
-
带有
type="module"
属性的<script>
标签-
非阻塞加载:带有
type="module"
的脚本加载是异步的,这类标签视为ES6模块来处理,而ES6模块是设计为异步加载的,当浏览器遇到此类标签时,会开始异步下载改模块及其依赖项,不会暂停页面的解析和渲染工作,当HTML文档被解析完成后,会在触发DOMContentLoaded
事件之前执行这些脚本。 -
模块化支持:可以将Javascript代码分割成独立模块,模块可以显示地声明它们所依赖的其它模块,浏览器会自动加载这些模块,无需手动管理依赖关系。
-
作用域隔离:在ES6模块中变量和函数默认不会污染全局作用域,它们会封装在模块内部,通过模块导出的接口对外可见。
-
支持静态导入和动态导入:可以使用
import
语句静态地导入其它模块,这些导入的模块加载时自动解析和执行。还可以使用import()
函数动态地导入模块,根据需要在运行时加载模块,进一步控制模块的加载和执行时机。
-
总结
只要HTML文档加载和解析完成,就会触发DOMContentLoaded
事件。而影响HTML文档解析的主要是<script>
标签,给<script>
标签设置不同的属性(defer、async、 type="module")对HTML文档的解析也有不同程度影响。
还有一点,加载CSS资源是否会影响HMTL文档解析? 本质上,CSS的加载不会影响HMTL的解析,但CSS的加载会阻塞JS的执行,如果JS执行过程中涉及到修改CSS的某个样式,就要等待相应的CSS下载修改完毕后才能继续往下执行。