关于页面加载

两个概念

  • DOM就绪:指浏览器已经接收到整个HTML并且DOM解析完成,这时就可以开始操作dom了,如绑定事件。
  • 渲染结束:浏览器已经接收到HTML中引用的所有样式文件、图片文件、以及Iframe等资源并渲染结束。

DOMContentLoaded

执行次序

DOMContentLoaded  -> angular启动 ->  onload

JQuery中这两个方法就是对DOMContentLoaded的监听

$(document).ready(callback)
$(function(){})

当页面处于“DOM就绪”状态时,就会执行DOMContentLoaded回调,通常推荐在DOMContentLoaded事件触发的时候为DOM元素注册事件。

测试发现

  可以认为dom就绪时,所有script也肯定已经就绪,script就绪意味着里面的同步脚本全都已经执行完毕。也就是说当DOMContendLoaded执行的时候,所有脚本(包括动态插入)中的同步代码都已经执行完了

 

注意:IE9以上才支持DOMContentLoaded

而对于IE6、7、8,如何监听DOM就绪这个事件呢?

ps: IE中使用attachEvent来绑定事件,而其他浏览器不支持这个方法,使用addEventListener来绑定事件

1.setTimeout

setTimeout(function(){
    console.log("in timeout 1")
});
window.addEventListener("DOMContentLoaded",function(){
    console.log("DOMContentLoaded");
});
setTimeout(function(){
    console.log("in timeout 2")
});
window.onload = function(){
    console.log("onload");
};

有两种运行结果(分别是有大图片资源和无大图片资源情况下):

timeout中和onload中代码执行的次序不确定,当资源(如大图片)等会延迟onload的执行,但可以明确的是,timeout中的代码都是在dom就绪之后才执行

 2.检测readyState为complete状态

setTimeout(function(){
    console.log("in timeout 1")
});
window.addEventListener("DOMContentLoaded",function(){
    console.log("DOMContentLoaded");
});
setTimeout(function(){
    console.log("in timeout 2")
});
window.onload = function(){
    console.log("onload");
};
document.onreadystatechange = function(){
    console.log(document.readyState)
}

两种运行结果(分别是无大图片资源和有大图片资源情况下):

可以发现规律:complete必定在DOMContentLoaded之后才执行,也就是说complete状态时,dom就已经就绪了

3.defer script标签

先了解一下defer和async,资料参考:defer和async的区别

我简单总结如下:

defer:html渲染完再执行,脚本间有序执行,能保证依赖关系

async:下载完就执行,不能保证有序,而且仅仅适用于外部脚本

  以上两者都是异步下载,即下载过程不影响html的渲染。如果不指定以上的属性(没有其一),也就是默认情况下,script的下载和执行会阻塞后续的html渲染和脚本执行,或者可以简单理解为:默认情况下,script没下载执行完成前,整个浏览器就都什么都不做了【既不渲染页面也不执行其他脚本了,针对这一点的优化策略是要么把js放到底部,要么使用setTimeout把耗时的操作延迟执行,这样就能避免影响其他资源的下载和dom渲染了】

  经过以上了解,可以发现,将script设置为defer,就可以保证该脚本在dom就绪后才执行了

测试发现:defer会在onload之前执行。而且使用以上两个属性后,script都是异步加载,异步加载的脚本中不允许出现document.write语句,否则报错:

Failed to execute 'write' on 'Document': It isn't possible to write into a document from an asynchronously-loaded external script unless it is explicitly opened.

而且在onload中执行document.write的话会完全覆盖原来的body内容。总结document.write的作用:onload前执行会往文档上添加内容,后执行会覆盖内容

补充onload 和 defer之间的关系:

  https://stackoverflow.com/questions/34753567/defer-attribute-and-onload-event

  https://stackoverflow.com/questions/5250412/how-exactly-does-script-defer-defer-work

onLoad

当页面所有元素都加载完毕后【所有脚本都加载和执行、图片加载完成】,才会触发onLoad。或者这个过程可以称为“渲染结束”

window.addEventListener("load", function(event) {
    console.log("All resources finished loading!");
});

onLoad回调通常被称为最后的回调

注意IE8及以下不支持addEventListener,需要使用attachEvent来绑定事件处理函数

 

指定onload方法有三种方式:

addEventListener("load",function(){
<body onload="o3()">
window.onload = function(){

谁先指定在前面,onload时就谁先执行。其中后两种方式如果同时指定的话,则先声明的会被后声明的覆盖

document.readyState

  • "loading":DOM在加载过程中;
  • "interactive":DOM就绪但资源仍在加载中;
  • "complete":DOM加载完成。

动态插入script

假设首页页面代码如下:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title></title>
    <script>
        function sleep(milliSeconds){
            let startTime = new Date().getTime();
            while(new Date().getTime() < startTime + milliSeconds){}
        }
        function log(msg){
            console.log(new Date().getSeconds() + "s - " + msg);
        }
        log("start")
    </script>
    <script src="1.js"></script>
    <script src="2.js"></script>
</head>
<body>
</body>
</html>

1.js

sleep(3000);

2.js

document.write('<script src="3.js"></script>');
window.addEventListener("DOMContentLoaded",function(){
    log("DOMContentLoaded");
});
window.onload = function(){
    log("onload");
};

3.js

log('3.js开始执行');
sleep(3000);

运行结果:

生成的html如下:

结论:

  sleep函数理解为 脚本中大量的耗时的同步操作

  DOMContentLoaded 会等待js脚本【包括scipt和动态加入的script标签】执行完后再触发事件回调,具体是为什么,往回看defer那一节就知道了

关于默认script是阻塞的两个原因

  1.脚本可能使用document.write来修改页面内容,因此浏览器会等待,以保证页面能正确布局

  2.保证脚本能按顺序到达浏览,从而能按顺序来执行

posted @ 2017-09-16 17:27  HelloHello233  阅读(458)  评论(0编辑  收藏  举报