关于js延迟加载(异步操做)的方式 addEventListener与attachEvent

1、概述

   最近从新开始学习js,在第一章的一个小节里写到了“脚本调用策略”,书上写的这部分很少,可是发如今我以前的(笔)面试中,问到的频率仍是比较高的。本身一直习惯于直接把全部js文件写在head里,后来了解到优化后,会把js放在最底部,但并不太懂这样作的好处,并且其余的一些处理方式,本身也并未有过实际的操做。javascript

  在面试中对这部分的考察,主要考察的是程序的性能方面。程序的性能是一个项目不断地追求的,一般也是项目完成后须要长期作的一件事情,为了让用户的体验更好。性能优化的核心思想就是快,能够预先准备数据(如缓存的使用),能够按需获取,能够分段获取等都是常见的优化手段。html

  而对于js的优化(关于js的延迟加载)的好处是有助于提升页面加载速度,js延迟加载就是等页面加载完成以后在加载js文件。java

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

示例:面试

<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>

上面的代码猛一看好像没啥问题,由于咱们老是很习惯这样作,可是运行后发现控制台报错。浏览器

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

 

2、js的同步加载和异步加载

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

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

3、js延迟加载的六种方式

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

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

(一)defer属性

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

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

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

而且defer属性解决了async引发的脚本顺序问题(见async的缺点),使用defer属性,脚本将按照在页面中出现的顺序加载和运行。

示例:

//脚本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以前。

(二)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中的函数,那么不肯定的调用顺序会致使错误。

因此,当页面的不一样脚本之间彼此独立,且不依赖于本页面的其余任何脚本时,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>

 

补充知识点:addEventListener与attachEvent

(四)使用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延迟方法

  目的:延迟加载js代码,给网页加载留出时间

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

(六)让js最后加载

   将脚本元素放在文档体的低端,这样脚本就能够在HTML解析完毕后加载了。但此方案的问题是,只有在全部HTML DOM加载完成后才开始脚本的加载/解析过程。对于有大量js代码的大型网站,可能会带来显著的性能损耗。

相关文章
相关标签/搜索