Web页面的生命周期
我们经常提到 Vue 的生命周期,在合适的生命周期做 DOM 操作是很重要的。除了了解框架的生命周期,原生 Web 页面的生命周期也是必须掌握的,比如最常遇到的 DOMContentLoaded、load,当然还有 beforeunload、unload 等等。
# 一、前言
理解 WEB 页面的生命周期,文档加载事件及顺序对 WEB 开发有十分的重要意义。如果不理解,在元素未加载就提前操作元素,则得不到想要的结果;而如果页面完全加载完成后,再进行操作,则又会影响用户体验。
一般来说,大多数的操作我们都应该放在 DOMContentLoaded 事件中执行。采用如下形式:
document.addEventListener("DOMContentLoaded", function () {
alert("DOMContentLoaded");
});
2
3
当初始的 HTML 文档被完全加载和解析完成之后,DOMContentLoaded 事件被触发,而无需等待样式表、图像和子框架的完成加载。
# 二、Web 页面生命周期演示
理解 WEB 页面的生命周期,文档加载事件及顺序对 WEB 开发有十分的重要意义。比如说在 JQuery 中,我们大多将代码写在$(document).ready()中,当文档加载完成后,就会执行该函数。如果不这样做,在文档未加载完成之前操作 DOM,就得不到想要的结果;在 Vue 中,我们对于 DOM 的操作大多放在 mounted(){}生命周期中,此时虚拟 DOM 已经渲染到真实页面上了。
演示程序如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>
HTML页面的生命周期,DOMContentLoaded、load、beforeunload、unload
</title>
</head>
<body>
<div id="main"></div>
<iframe
src="http://res.42du.cn/static/html/iframe.html"
height="35"
onload="append('iframe onload')"
></iframe>
<div>
<img
src="http://res.42du.cn/up/201802/cbbtiga6.jpg?cache=none"
onload="append('image onload')"
/>
</div>
<script>
var output = "";
function print() {
document.getElementById("main").innerHTML += output;
}
function append(data) {
output += data + " <br />";
}
document.addEventListener("readystatechange", function () {
append("readyState : " + document.readyState);
});
document.addEventListener("DOMContentLoaded", function () {
append("DOMContentLoaded");
});
window.onload = function () {
append("window onload");
print();
};
append("script executed");
</script>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
从演示程序运行结果,得出一般浏览器的加载顺序如下:
- script executed
- readyState : interactive
- DOMContentLoaded
- image onload
- iframe onload
- readyState : complete
- window onload
从以上可以看出痛的 script 总是先于其它事件执行,而 window.onload 事件总是最后执行;而 imgae onload 和 iframe onload 的先后顺序并不确定。
# 三、Web 页面生命周期总结
关于 WEB 页面的生命周期,有以下特点:
- 同步的 JavaScript 脚本最先执行,它先于 DOMContentLoaded 事件执行;
- 当 DOM 准备就绪时,DOMContentLoaded 事件在 document 上触发,可以在这个阶段利用 JavaScript 来操作 DOM 元素;
- 所有脚本都执行完毕,除了那些外部使用异步(async)或延迟(defer)加载的脚本;
- 图片和其他资源可能仍在载入过程中。
- window 的 onload 事件,在页面加载所有资源后触发;一般很少使用它,因为通常的操作不用等到最后才执行。
- window 的 beforeunload 事件,该事件在用户准备离开页面,在 unload 事件之前触发。如果 beforeunload 返回一个字符串,浏览器会给出用户是否真的想离开的提示。
- window 的 unload 事件,当用户最终离开时会触发该事件。在 unload 的事件处理程序中,只能做简单的事情,不涉及延迟或询问用户。由于这个限制,它很少使用。
- document.readyState 表示文档的当前状态,可以在 readystatechange 事件中跟踪文档状态的变更:
- loading:文档正在载入
- interactive:document 已经解析完毕时触发,几乎与 DOMContentLoaded 同时发生,但在 DOMContentLoaded 事件之前触发;
- complete:文档和资源加载完成时触发,几乎与 window.onload 同时发生,但在 onload 事件之前触发。
一般来说,大多数的操作我们都应该放在 DOMContentLoaded 事件中执行,而不要放在 window.onload 中执行。
# 四、async、defer 与 DOMContentLoaded 顺序
除了同步的JS脚本外,还有异步的脚本,可以通过声明async
或defer
属性来异步加载脚本。
# 1. 带async的script
async脚本的加载不计入DOMContentLoaded事件统计,也就是说下图两种情况都是有可能发生的:
- HTML 还没有被解析完的时候,async脚本已经加载完了,那么 HTML 停止解析,去执行脚本,脚本执行完毕后触发DOMContentLoaded事件;
带async的script-1 (opens new window)
- HTML 解析完了之后,async脚本才加载完,然后再执行脚本,那么在HTML解析完毕、async脚本还没加载完的时候就触发DOMContentLoaded事件。
带async的script-2 (opens new window)
# 2. 带defer的script
文档解析时,遇到设置了defer的脚本,就会在后台进行下载,但是并不会阻止文档的渲染,当页面解析和渲染完毕后,会等到所有的defer脚本加载完毕并按照顺序执行完毕才会触发DOMContentLoaded事件,也就是说下图两种情况都是有可能发生的:
- HTML 还没有被解析完的时候,defer脚本已经加载完了,那么 等待HTML 解析完成后执行脚本,脚本执行完毕后触发DOMContentLoaded事件。
- HTML 解析完了之后,defer脚本才加载完,然后再执行脚本,脚本执行完毕后触发DOMContentLoaded事件。
参考文章:
图解<script>
的 defer / async 与 DOMContentLoaded / Load (opens new window)
WEB 页面的生命周期,DOMContentLoaded,load,beforeunload,unload (opens new window)