浏览器页面的生命周期
总结
页面事件的生命周期:
-
DOMContentLoaded事件在DOM树构建完毕后被触发,我们可以在这个阶段使用js去访问元素。
-
async和defer的脚本可能还没有执行。
-
图片及其他资源文件可能还在下载中。
-
load事件在页面所有资源被加载完毕后触发,通常我们不会用到这个事件,因为我们不需要等那么久。
-
beforeunload在用户即将离开页面时触发,它返回一个字符串,浏览器会向用户展示并询问这个字符串以确定是否离开。
-
unload在用户已经离开时触发,我们在这个阶段仅可以做一些没有延迟的操作,由于种种限制,很少被使用。
-
document.readyState表征页面的加载状态,可以在readystatechange中追踪页面的变化状态:
-
loading — 页面正在加载中。
-
interactive -- 页面解析完毕,时间上和 DOMContentLoaded同时发生,不过顺序在它之前。
-
complete -- 页面上的资源都已加载完毕,时间上和window.onload同时发生,不过顺序在他之前。
HTML页面的生命周期有以下三个重要事件:
-
DOMContentLoaded — 浏览器已经完全加载了HTML,DOM树已经构建完毕,但是像是 <img> 和样式表等外部资源可能并没有下载完毕。
-
load — 浏览器已经加载了所有的资源(图像,样式表等)。
-
beforeunload/unload -- 当用户离开页面的时候触发。
每个事件都有特定的用途
-
DOMContentLoaded -- DOM加载完毕,所以js可以访问所有DOM节点,初始化界面。
-
load -- 附加资源已经加载完毕,可以在此事件触发时获得图像的大小(如果没有被在HTML/CSS中指定)
-
beforeunload/unload -- 用户正在离开页面:可以询问用户是否保存了更改以及是否确定要离开页面。
DOMContentLoaded 似乎很简单,DOM树构建完毕之后就运行该事件,不过其实存在一些陷阱。
DOMContentLoaded 和脚本
当浏览器在解析HTML页面时遇到了 <script>...</script> 标签,将无法继续构建DOM树(译注:UI渲染线程与JS引擎是互斥的,当JS引擎执行时UI线程会被挂起),必须立即执行脚本。所以 DOMContentLoaded 有可能在所有脚本执行完毕后触发。
外部脚本(带src的)的加载和解析也会暂停DOM树构建,所以 DOMContentLoaded 也会等待外部脚本。
不过有两个例外是带async和defer的外部脚本,他们告诉浏览器继续解析而不需要等待脚本的执行,所以用户可以在脚本加载完成前可以看到页面,有较好的用户体验。
async和defer属性仅仅对外部脚本起作用,并且他们在src不存在时会被自动忽略。
它们都告诉浏览器继续处理页面上的内容,而在后台加载脚本,然后在脚本加载完毕后再执行。所以脚本不会阻塞DOM树的构建和页面的渲染。
(译注:其实这里是不对的,带有async和defer的脚本的下载是和HTML的下载与解析是异步的,但是js的执行一定是和UI线程是互斥的,像下面这张图所示,async在下载完毕后的执行会阻塞HTML的解析)
他们有两处不同:
|
async
|
defer
|
顺序
|
带有async的脚本是优先执行先加载完的脚本,他们在页面中的顺序并不影响他们执行的顺序。
|
带有defer的脚本按照他们在页面中出现的顺序依次执行。
|
DOMContentLoaded
|
带有async的脚本也许会在页面没有完全下载完之前就加载,这种情况会在脚本很小或本缓存,并且页面很大的情况下发生。
|
带有defer的脚本会在页面加载和解析完毕后执行,刚好在DOMContentLoaded之前执行。
|
所以async用在那些完全不依赖其他脚本的脚本上。
浏览器的自动补全
Firefox, Chrome和Opera会在DOMContentLoaded执行时自动补全表单。
例如,如果页面有登录的界面,浏览器记住了该页面的用户名和密码,那么在 DOMContentLoaded运行的时候浏览器会试图自动补全表单(如果用户设置允许)。
所以如果DOMContentLoaded被一个需要长时间执行的脚本阻塞,那么自动补全也会等待。你也许见过某些网站(如果你的浏览器开启了自动补全)—— 浏览器并不会立刻补全登录项,而是等到整个页面加载完毕后才填充。这就是因为在等待DOMContentLoaded事件。
使用带async和defer的脚本的一个好处就是,他们不会阻塞DOMContentLoaded和浏览器自动补全。(译注:其实执行还是会阻塞的)
window.onload
window对象上的onload事件在所有文件包括样式表,图片和其他资源下载完毕后触发
window.onunload
用户离开页面的时候,window对象上的unload事件会被触发,我们可以做一些不存在延迟的事情,比如关闭弹出的窗口,可是我们无法阻止用户转移到另一个页面上。
所以我们需要使用另一个事件 — onbeforeunload。
window.onbeforeunload
如果用户即将离开页面或者关闭窗口时,beforeunload事件将会被触发以进行额外的确认。
readyState
如果我们在整个页面加载完毕后设置DOMContentLoaded会发生什么呢?
啥也没有,DOMContentLoaded不会被触发。
有一些情况我们无法确定页面上是否已经加载完毕,比如一个带有async的外部脚本的加载和执行是异步的(注:执行并不是异步的-_-)。在不同的网络状况下,脚本有可能是在页面加载完毕后执行也有可能是在页面加载完毕前执行,我们无法确定。所以我们需要知道页面加载的状况。
document.readyState属性给了我们加载的信息,有三个可能的值:
-
loading 加载 - document仍在加载。
-
interactive 互动 - 文档已经完成加载,文档已被解析,但是诸如图像,样式表和框架之类的子资源仍在加载。
-
complete - 文档和所有子资源已完成加载。状态表示 load 事件即将被触发。
所以我们可以检查 document.readyState 的状态,如果没有就绪可以选择挂载事件,如果已经就绪了就可以直接立即执行。
readystatechange的在各个事件中的执行顺序又是如何呢?
输出如下:
-
[1] initial readyState:loading
-
[2] readyState:interactive
-
[2] DOMContentLoaded
-
[3] iframe onload
-
[4] readyState:complete
-
[4] img onload
-
[4] window onload
方括号中的数字表示他们发生的时间,真实的发生时间会更晚一点,不过相同数字的时间可以认为是在同一时刻被按顺序触发(误差在几毫秒之内)
--------------------------文章二-------------------------
前言
javascript 主要是运行在客户端浏览器中的。专业点说,浏览器是js运行的宿主环境,就好比浏览器是地球,js是人类,地球的生命多长,人类的生命也就是多长。地球毁灭,人类也就完蛋了。
当然,就像美国大片一样,人类总是幻想在地球毁灭之前,能够找到新的栖息地,虽然这对于人类还很遥远,但js已经找到了脱离浏览器,在服务器端运行的方式,就是Node。。。。
这里不讨论node,而是讨论客户端浏览器的生命周期,进而明白js的执行情况。
总体来说,页面的生命周期从点击“超链接”或地址栏中输入URL,前往页面开始,到关闭浏览器窗口结束。
在整个生命周期中,主要是处理了页面构建和事件处理两个阶段。
页面构建
浏览器处理从服务器端获取的html、css和js代码,整个处理过程可以概括为下面几点
-
浏览器按照从上到下的顺序执行代码
-
浏览器遇到html代码,就会按照html构建dom,遇到script 标记,就执行其中的js代码
这里要注意几点
事件处理
用户移动鼠标、点击鼠标、以及发送的ajax请求都属于事件,浏览器还要负责这些事件的处理
注册事件处理器
事件处理器就是事件发生时执行的函数,注册事件处理器,就是将函数与对应的事件关联起来,推荐使用addEventListener方法注册事件
<html>
<head>
<title>test</title>
</head>
<body>
<p id="p1">where am i</p>
<script>
document.getElementById('p1').addEventListener('click',function(){
this.textContent="click me";
})
</script>
</body>
</html>
事件队列
注册事件后,会将事件放到一个事件队列中,浏览器有一个循环机制,总是检查事件队列的队首,如果有事件,则执行,执行完毕后,从队列中将此事件删除,下一个事件递进到队首,浏览器继续执行。一直到事件队列中没有事件,浏览器也会一致检查,直到有事件后继续执行整个事件
全局对象
如果页面中存在多个script标签,就会有多端script 代码,在其中一段中声明的变量,在所有script 标签中都是可以使用的,这是因为有一个全局对象window ,这个全局对象的作用域就是整个页面。
<html>
<head>
<title>test</title>
</head>
<body>
<p id="p1">where am i</p>
<script>
var a=10;
</script>
<p>hello</p>
<script>
document.getElementById('p1').textContent=a;
</script>
</body>
</html>