前端页面生命周期
前言
对页面声明周期的总结与回忆。
下文均为个人测试得出的结论,如有不对望指出。
正文
了解页面声明周期面前,需要了解几个概念。
1.在页面中dom加载与css加载是异步的。但是呢,也不绝对,比如内联css是同步的。
2.页面中js加载与dom加载是同步的,但是也不绝对,这个比较复杂后文介绍。
先看下页面的生命周期:
DomContentLoaded:dom加载完毕,但是外部资源可能没有加载完毕,如样式表、图片资源等。
load:浏览器所有资源加载完毕。
beforunload/unload 当用户离开页面的时候触发。
在我们写demo的时候,往往这样写:
window.onload=function(){
}
可能会把我们初始化的资源都写在onload中,但是在正常项目中,这样写的效率是不高的。
因为DomContentLoaded 这个时候就已经加载完了我们的Dom,这个时候就可以操作dom了。
但是因为样式没有加载,如果需要动态去获取样式的一些属性,那么就需要在onload中了。
DOMContentLoaded
用法:
document.addEventListener("DOMContentLoaded",ready);
function ready()
{
console.log("ready");
}
因为前面提及到dom加载与资源加载是异步的,这时候不要去拿取资源,所以不要去拿取资源文件。
前面提及UI渲染线程与JS引擎是互斥的,即使script 引用是以资源文件src方式引用的,这时候依然会等待js的加载。
这时候就会有一些小问题,如果js加载时候过程过于漫长,那么这样用户会不会以为宕机了,会不会怀疑有人删库跑路了?
如何提高用户体验度?
这时候就有async 与 defer 来帮助了。
<script type="text/javascript" src="demo_async.js" async="async"></script>
<script type="text/javascript" defer="defer">
alert(document.getElementById("p1").firstChild.nodeValue);
</script>
对了,async只是针对外部资源,也就是对src有效,内联js无效,defer对内联与src都有效。
<script src="./test.js" async='async'>
</script>
<script>
document.addEventListener("DOMContentLoaded",ready);
function ready()
{
console.log("ready");
}
</script>
test.js
console.log("out script");
defer,我就不演示了,来介绍这两个有什么不同吧。
名称 | 特性 |
---|---|
async | 比如有几个带有async的js文件,这几个执行文件顺序是不知道的,也就是说async的资源文件是异步的 |
defer | 会根据我们在页面中书写的顺序执行 |
但是我建议不使用defer,看起来defer更好,实际上defer有致命性问题,只有 Internet Explorer 支持 defer 属性。
看到这里开心不开心?又少学了一样东西。
但是呢,这个async 水有点深,怎么说呢?
我刚才提及到了DOMContentLoaded会先执行,其实也不一定。
为何这么说呢?比如我们页面有很多不是async的js,然后可能async的资源文件还有缓存,当async加载完毕后,那么不就要先执行async的资源。
所以async应该理解为独立于dom生命周期加载执行。
举一个async的一个好处的例子,也是我在网上看到的。
Firefox, Chrome和Opera会在DOMContentLoaded执行时自动补全表单。
但是当人们看到页面的时候表单还没有补齐。这时候就是DOMContentLoaded被前面执行的js给堵塞了。
这里提及到另外一个现代主义浏览器的问题,为什么我们将js脚本放在body最后加载,是否可以减少到达DOMContentLoaded的时间?
其实不能,我们知道让js放在dom元素可以让docuemnt.getElementById 可以拿到元素,但是这依然可以在DOMContentLoaded中执行,似乎起不好什么优化效果。
相对以前的浏览器,现在浏览器不对等待dom加载完毕后渲染,而是会先加载渲染一部分。
但是同样有问题,比如我们要操作一些dom,但是dom太多,我们的js又在最后,那么很有可能会乱。
所以我们打开大型网页的时候,常常见到嵌入到dom元素直接的。
像这样:
<div></div>
<script></script>
<div></div>
但是,DOMContentLoaded依然要等到dom加载完毕。在这里,其实DOMContentLoaded还没有介绍完,因为还有ie的兼容问题会在下面提及。
window.onload
这个不多介绍了,window对象上的onload事件在所有文件包括样式表,图片和其他资源下载完毕后触发。
window.onbeforeunload
有些网页在我们在离开的时候,会问我们是否保存。
就是用这个实现的。
支持情况:
IE、Safari 完美支持
Firefox、Chrome 不支持文字提醒信息
Opera 不支持
window.onunload
用户离开页面的时候使用。
支持情况:
IE、Safari 完美支持
Firefox、Chrome 不支持文字提醒信息
Opera 不支持
readyState
这个用的并不多,一般用来兼容ie的。
但还有有些用的。比如我们使用async,异步的时候希望去操作dom,不知道async是在加载后执行还是加载前,所以可以这样。
if (document.readyState == 'loading') {
document.addEventListener('DOMContentLoaded', dosomething);
} else {
dosomething();
}
function dosomething() { /*.xxxxx..*/ }
回过来看下有什么状态:
一个document 的 Document.readyState 属性描述了文档的加载状态。
loading / 正在加载
document 仍在加载。
interactive / 可交互
文档已被解析,"正在加载"状态结束,但是诸如图像,样式表和框架之类的子资源仍在加载。
complete / 完成
文档和所有子资源已完成加载。表示 load 状态的事件即将被触发。
当这个属性的值变化时,document 对象上的readystatechange 事件将被触发。
这样我们可以监听到html的加载状态了。
document.addEventListener('readystatechange', () => log('readyState:' + document.readyState));
有一点上面可能产生误解:是image的onload先执行呢?还是document.readyState 的complete 先执行呢?
这个不一定。因为如果image是最后一个加载的资源,回调的可能就先是complete。如果image不是最后一个加载的资源,那么就是image的onload了。
但是complete 回调的时候肯定资源都加载完成了。
本来打算在这里介绍ie兼容的,因为Document.readyState是一个非常常用兼容处理,但是篇幅问题,就下次吧。
总结
以上来自个人理解,如有不对,望指出。