• 关于页面加载


    两个概念

    • 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.保证脚本能按顺序到达浏览,从而能按顺序来执行

  • 相关阅读:
    代码坏味道
    外包对程序员发展有什么弊端?
    前端接口神器
    使用Autofac
    在 WASI 上运行 .NET 7 应用程序
    在非k8s 环境下 的应用 使用 Dapr Sidekick for .NET
    一个简单的模拟实例说明Task及其调度问题
    xxljob 小结
    [LeetCode] 1291. Sequential Digits 顺次数
    [LeetCode] 1289. Minimum Falling Path Sum II 下降路径最小和之二
  • 原文地址:https://www.cnblogs.com/hellohello/p/7531904.html
Copyright © 2020-2023  润新知