zoukankan      html  css  js  c++  java
  • js延迟(异步)加载的6种方式

    为什么要延迟加载js呢?

      对于js的优化(关于js的延迟加载)的好处是有助于提高页面加载速度js延迟加载就是等页面加载完成之后在加载js文件

      之所以要优化是因为HTML元素是按其在页面中出现的次序调用的,如果用javascript来管理页面上的元素(使用文档对象模型dom),并且js加载于欲操作的HTML元素之前,则代码将出错。也就是说,我们写了js语句来获取DOM对象,但由于DOM结构还没有加载完成,因此获取到的是空对象

    举个栗子吧~

    <head>
        <script type="text/javascript">
            var ul = document.getElementsByTagName('ul')[0]; //获取ul
            var list = ul.getElementsByTagName('li');
            for(var i =0;i<list.length;i++){
                ul.appendChild(document.createElement('li'));
            }
        </script>
    </head>
    <body>
    <ul>
        <li>111</li>
        <li>222</li>
        <li>333</li>
    </ul>
    </body>
    </html>

    你平时好像就是这么写的,但是你会发现控制台会报错:Uncaught TypeError :Cannot read property 'getElementByTagName' of undefined at... 

    这就是因为js加载执行于DOM结构之前,所以获取不到。简单的解决办法是把<script>放在<body>后面。

    js的同步加载和异步加载

     同步加载,又称阻塞模式,是我们平时使用最多的方式,也就是直接将<script>写在<head>里。这种方式会阻止浏览器的后续处理,停止后续的解析,直到当前的加载完成。一般来说,同步加载是安全的,但如果我们js里设计到document内容输出、获取或修改DOM结构等行为,就会产生页面阻塞代码出错。所以一般就会建议把<script>写在页面最底部,以减少页面阻塞。(这种方式可能也是我们刚开始接触到js优化,最常使用的一种方式。)

      异步加载,又称为非阻塞加载,在浏览器下载执行js的同时,还会继续后续页面的处理。这里也是一般面试会问到的一点,即js延迟加载的方式有哪些?

    js延迟加载的六种方式

      一般有六种方式;defer属性、async属性、动态创建dom方式、使用jquery的getScript方法、使用setTimeout延迟方法、让js最后加载。

      写的是六种方式,实际上自己在项目中真实用到的也就是让js最后加载。所以对这所谓的六种方式,可能仅作为一种知识储备,当以后的项目有这种问题需求了,可以有不同的解决思路。

    一、defer属性

    HTML 4.01为 <script>标签定义了defer属性(延迟脚本的执行)。

    其用途是:表明脚本在执行时不会影响页面的构造,浏览器会立即下载,但延迟执行,即脚本会被延迟到整个页面都解析完毕之后再执行

    defer属性只适用于外部脚本文件,只有 Internet Explorer 支持 defer 属性

    并且defer属性解决了async引起的脚本顺序问题,使用defer属性,脚本将按照在页面中出现的顺序加载和运行。

    示例1:

    //脚本1
    <script defer src="js/vendor/jquery.js"></script>
    //脚本2
    <script defer src="js/script2.js"></script>
    //脚本3
    <script defer src="js/script3.js"></script>​

     上述代码添加 defer 属性,脚本将按照在页面中出现的顺序加载,因此可确保脚本1必定加载于脚本2和 脚本3之前,同时脚本2必定加载于脚本3之前。(补充:最近在看《高级程序设计》里面说,实际上延迟脚本并不一定会按照顺序执行,也不一定会在DOMContenterLoaded事件触发前执行,因此最好只包含一个延迟脚本

    示例2:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>defer练习</title>
    </head>
    <script type="text/javascript" defer="defer" src="example1.js"></script>
    <script type="text/javascript" defer="defer" src="example2.js"></script>
    <body>
    <!--这里是内容-->
    </body>
    </html>

    在上面的例子中,虽然把<script>放在里<head>里,但是包含的脚本会延迟到整个页面之后,即延迟到浏览器遇到</html>标签后再执行。同时,在XHTML文档中,要把defer属性设置为defer="defer"

    二、async属性

    HTML 5为 <script>标签定义了async属性。添加此属性后,脚本和HTML将一并加载(异步),代码将顺利运行

    浏览器遇到async脚本时不会阻塞页面渲染,而是直接下载然后运行。但这样的问题是,不同脚本运行次序就无法控制,只是脚本不会阻止剩余页面的显示

    async属性只适用于外部脚本文件。

    示例:

    //脚本1
    <script async src="js/vendor/jquery.js"></script>
    //脚本2
    <script async src="js/script2.js"></script>
    //脚本3
    <script async src="js/script3.js"></script>​

    上述代码添加async 属性,这三者的调用顺序是不确定的,脚本1可以在脚本2和脚本3之前会之后调用,这是完全不确定的。如果脚本2和脚本3需要依赖脚本1中的函数,那么不确定的调用顺序会导致错误。(补充:异步脚本一定会在页面的load事件前执行,但可能会在DOMContenterLoaded事件触发之前或之后执行)

    所以,当页面的不同脚本之间彼此独立,且不依赖于本页面的其他任何脚本时,async是最理想的选择

    总结:defer和async的异同点

    相同:

    • 加载文件时不会阻塞页面渲染
    • 对于内部的js不起作用
    • 使用这两个属性的脚本中不能调用document.write方法

    区别:

    • 如果脚本无需等待页面解析,且无依赖独立运行,那么应使用 async。也就是每一个async属性的脚本都在它下载结束之后立即执行,同时会在window的load事件之前执行。
    • 如果脚本需要等待解析,且依赖于其它脚本,调用这些脚本时应使用 defer,将关联的脚本按所需顺序置于 HTML 中。

    三、动态创建DOM方式

    //这些代码应被放置在</body>标签前(接近HTML文件底部)
    <script type="text/javascript">
    function downloadJSAtOnload(){
        var element = document.createElement("script");
        element.src = "defer.js";
        document.body.appendChild(element); 
    }
    if (window.addEventListener) //添加监听事件
        window.addEventListener("load",downloadJSAtOnload,false);   //事件在冒泡阶段执行
    else if (window.attachEvent) 
        window.attachEvent("onload",downloadJSAtOnload);
    else 
        window.onload =downloadJSAtOnload;
    </script>

    四、使用jquery的getScript方法

    getScript() 方法通过 HTTP GET 请求载入并执行 JavaScript 文件。

      语法:jQuery.getScript(url,success(response,status))

      url(必写):将要请求的 URL 字符串
      success(response,status)(可选):规定请求成功后执行的回调函数。
      其中的参数
      response - 包含来自请求的结果数据
      status - 包含请求的状态("success", "notmodified", "error", "timeout" 或 "parsererror")

    //加载并执行 test.js:
    $.getScript("test.js");
    //加载并执行 test.js ,成功后显示信息
    $.getScript("test.js", function(){
      alert("Script loaded and executed.");
    });

    五、使用setTimeout延迟方法

    <script type="text/javascript">
      function A(){
        $.post("/lord/login",{name:username,pwd:password},function(){
          alert("Hello World!");
        })
      }
      $(function (){
        setTimeout("A()",1000); //延迟1秒
      })
    </script>

    六、让js最后加载

       将脚本元素放在文档体的底端(</body>标签前面),这样脚本就可以在HTML解析完毕后加载了。但此方案的问题是,只有在所有HTML DOM加载完成后才开始脚本的加载/解析过程。对于有大量js代码的大型网站,可能会带来显著的性能损耗。

  • 相关阅读:
    区块链技术栈-区块链账本
    (引文)可扩展的分布式数据库架构
    CentOS7 通过systemd 添加开机重启服务
    spring发布RMI服务(-)
    使用jdbc连接上oracle的两种方法
    用户态和内核态
    MySQL数据库备份还原(基于binlog的增量备份)
    分布式事务
    shuffle 过程
    MapReduce的流程
  • 原文地址:https://www.cnblogs.com/yaya-003/p/12708726.html
Copyright © 2011-2022 走看看