前言

对页面声明周期的总结与回忆。

下文均为个人测试得出的结论,如有不对望指出。

正文

了解页面声明周期面前,需要了解几个概念。

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是一个非常常用兼容处理,但是篇幅问题,就下次吧。

总结

以上来自个人理解,如有不对,望指出。