zoukankan      html  css  js  c++  java
  • document.createElement('script') 和 DOMContentLoaded 执行顺序

    上篇在翻译一篇文章时看到:

    脚本不阻塞DOMContentLoaded

    此规则有两个特例:

    1. 脚本有 async 属性,我们稍后会提到此属性,不会阻塞 DOMContentLoaded
    2. 脚本由 document.createElement('script') 动态生成,然后加入到html文档,也不会阻塞 DOMContentLoaded

    上边提到的第一条规则我之前是知道的: async 脚本会在 load 事件之前发生,但不能保证 DOMContentLoaded 事件先后的执行顺序 ,也就是说不一定阻塞渲染(如果文件过大,下载时间长(浏览器渲染和js文件下载并行执行),有可能在浏览器渲染完成后才下载完,也就是在DOMContentLoaded事件触发后才执行)

    那么,第二条规则的我之前没有留意过,有可能跟平时使用的少有关(几乎没有使用过)。我第一时间会质疑:这个说法成立吗?

    实践出真知,实例代码如下:

    loadScripts([
        './dynamic-script.js',
      ], function () {
        alert('dynamic-script: loaded')
      })
    
      document.addEventListener('DOMContentLoaded', () => {
        alert('DOMContentLoaded')
      })
      
      window.onload = () => {
        alert('onload')
      }
    
      function loadScripts(array,callback){
        var loader = function(src,handler){
          var script = document.createElement("script");
          script.src = src;
          script.onload = script.onreadystatechange = function(){
            script.onreadystatechange = script.onload = null;
            handler();
    	console.log(script.async) // 注1
          }
          var head = document.getElementsByTagName("head")[0];
          (head || document.body).appendChild( script );
        };
        (function run(){
          if(array.length!=0){
            loader(array.shift(), run);
          }else{
            callback && callback();
          }
        })();
      }
    
    // dynamic-script.js
    alert('dynamic-script-content')
    

    执行结果如下:

    1. 打印 'DOMContentLoaded'
    2. 打印 'dynamic-script-content'
    3. 打印 'dynamic-script: loaded'
    4. 打印 true(控制台)
    5. 打印 'onload'

    结果跟上边的描述是一致的。。。

    网上查阅资料解释:JavaScript异步加载的三种方式——async和defer、动态创建script

    在没有定义defer和async之前,异步加载的方式是动态创建script,通过window.onload方法确保页面加载完毕再将script标签插入到DOM中。

    MDN解释: <script>

    [1] 在不支持该async属性的旧浏览器中,解析器插入的脚本会阻塞解析器;插入脚本的脚本在 IE 和 WebKit 中异步执行,但在 Opera 和 4.0 之前的 Firefox 中同步执行。在 Firefox 4.0 中,asyncDOM 属性默认为true用于脚本创建的脚本,因此默认行为与 IE 和 WebKit 的行为相匹配。要请求在document.createElement("script").async评估为的浏览器true(例如 Firefox 4.0)中以插入顺序执行插入脚本的外部脚本,请.async=false在要维护顺序的脚本上设置。永远不要document.write()从async脚本中调用。在 Gecko 1.9.2 中,调用document.write()具有不可预测的效果。在 Gecko 2.0 中,document.write()从async 脚本无效(除了向错误控制台打印警告)。

    此外,还有一篇文章提到: DOMContentLoaded talk about blocking and rendering - analysis and resource loading html page events

    为了验证上边的解释,我在实例代码中(// 注1)处对script的async值进行了输出,由 document.createElement("script") 创建的脚本确实是异步的。

    记忆技巧

    首先我们来看 document.createElement("script") ,这里边有个 document... 也就是说我们需要使用 document 对象,何时才能访问到 document 对象? 答案显而易见:dom树构建完毕时(此时页面还没有生成cssom,render tree,布局,绘制balabala)。。。我们考虑下这段动态插入的脚本肯定不是很重要的(如果很重要,需要在关键渲染路径之前执行,肯定会写为内联脚本),既然这段脚本不是很重要,那就不能影响关键渲染路径的性能,那就等浏览器首屏渲染完成后(这句可能不那么准确)再下载执行也不迟。。。所以设计者就将其放在 DOMContentLoaded 之后下载执行,从而实现异步加载,也就是不阻塞 DOMContentLoaded 事件。

  • 相关阅读:
    c++ 指定目录下的文件遍历
    c++ 实现键盘钩子
    c++ 用模板类实现顺序储存的线性表
    c++ 递归算法实现排列组合
    matlab 基础知识
    QT 给工程添加图片
    QT5.9 QString和字符串转换的乱码问题
    【内核】——进程3,内核同步
    Java多线程——Thread的native底层实现
    【内核】——文件和文件系统的内部结构4 系统调用的实现
  • 原文地址:https://www.cnblogs.com/hanshuai/p/14925085.html
Copyright © 2011-2022 走看看