咱们平时使用的最多的一种方式。javascript
<script src="http://yourdomain.com/script.js"></script> <script src="http://yourdomain.com/script.js"></script>
同步模式,又称阻塞模式,会阻止浏览器的后续处理,中止后续的解析,只有当当前加载完成,才能进行下一步操做。因此默认同步执行才是安全的。但这样若是js中有输出document内容、修改dom、重定向等行为,就会形成页面堵塞。因此通常建议把<script>标签放在<body>结尾处,这样尽量减小页面阻塞。php
异步加载又叫非阻塞加载,浏览器在下载执行js的同时,还会继续进行后续页面的处理。主要有三种方式。css
(function(){ var scriptEle = document.createElement("script"); scriptEle.type = "text/javasctipt"; scriptEle.async = true; scriptEle.src = "http://cdn.bootcss.com/jquery/3.0.0-beta1/jquery.min.js"; var x = document.getElementsByTagName("head")[0]; x.insertBefore(scriptEle, x.firstChild); })();
<async>属性是HTML5中新增的异步支持。此方法被称为Script DOM Element 方法。Google Analytics 和 Google+ Badge 都使用了这种异步加载代码.html
(function(){; var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'; var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s); })();
可是这种加载方式执行完以前会阻止onload事件的触发,而如今不少页面的代码都在onload时还执行额外的渲染工做,因此仍是会阻塞部分页面的初始化处理。html5
(function(){ if(window.attachEvent){ window.attachEvent("onload", asyncLoad); }else{ window.addEventListener("load", asyncLoad); } var asyncLoad = function(){ var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'; var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s); } )();
这种方法只是把插入script的方法放在一个函数里面,而后放在window的onload方法里面执行,这样就解决了阻塞onload事件触发的问题。java
注:DOMContentLoaded与load的区别。前者是在document已经解析完成,页面中的dom元素可用,可是页面中的图片,视频,音频等资源未加载完,做用同jQuery中的ready事件;后者的区别在于页面全部资源所有加载完毕。jquery
//获取XMLHttpRequest对象,考虑兼容性。 var getXmlHttp = function(){ var obj; if (window.XMLHttpRequest) obj = new XMLHttpRequest(); else obj = new ActiveXObject("Microsoft.XMLHTTP"); return obj; }; //采用Http请求get方式;open()方法的第三个参数表示采用异步(true)仍是同步(false)处理 var xmlHttp = getXmlHttp(); xmlHttp.open("GET", "http://cdn.bootcss.com/jquery/3.0.0-beta1/jquery.min.js", true); xmlHttp.send(); xmlHttp.onreadystatechange = function(){ if (xmlHttp.readyState == 4 && xmlHttp.status == 200){ var script = document.createElement("script"); script.text = xmlHttp.responseText; document.getElementsByTagName("head")[0].appendChild(script); } }
与XHR Injection对responseText的执行方式不一样,直接把responseText放在eval()函数里面执行。ajax
//获取XMLHttpRequest对象,考虑兼容性。 var getXmlHttp = function() { var obj; if (window.XMLHttpRequest) obj = new XMLHttpRequest(); else obj = new ActiveXObject("Microsoft.XMLHTTP"); return obj; }; //采用Http请求get方式;open()方法的第三个参数表示采用异步(true)仍是同步(false)处理 var xmlHttp = getXmlHttp(); xmlHttp.open("GET", "http://cdn.bootcss.com/jquery/3.0.0-beta1/jquery.min.js", true); xmlHttp.send(); xmlHttp.onreadystatechange = function() { if (xmlHttp.readyState == 4 && xmlHttp.status == 200) { eval(xmlHttp.responseText); //alert($);//能够弹出$,代表JS已经加载进来。click事件放在其它出会出问题,应该是还没加载进来 $("#btn1").click(function() { alert($(this).text()); }); } }
在父窗口插入一个iframe元素,而后再iframe中执行加载JS的操做。浏览器
var insertJS = function(){alert(2)}; var iframe = document.createElement("iframe"); document.body.appendChild(iframe); var doc = iframe.contentWindow.document;//获取iframe中的window要用contentWindow属性。 doc.open(); doc.write("<script>var insertJS = function(){};<\/script><body onload='insertJS()'></body>"); doc.close();
页内JS内容被注释,因此不会执行,在须要的时候,获取script中的text内容去掉注释,调用eval()执行。缓存
<script type="text/javascript"> /* var ... */ </script>
defer属性:IE4.0就出现。defer属声明脚本中将不会有document.write和dom修改。浏览器会并行下载其余有defer属性的script。而不会阻塞页面后续处理。注:全部的defer脚本必须保证按顺序执行的。
<script type="text/javascript" defer></script>
async属性:Html5新属性。脚本将在下载后尽快执行,做用同defer,可是不能保证脚本按顺序执行。他们将在onload事件以前完成。
<script type="text/javascript" async></script>
Firefox 3.六、Opera 10.五、IE 9和最新的Chrome和Safari都支持async属性。能够同时使用async和defer,这样IE 4以后的全部IE都支持异步加载。
没有async属性,script将当即获取(下载)并执行,期间阻塞了浏览器的后续处理。若是有async属性,那么script将被异步下载并执行,同时浏览器继续后续的处理。
总结: 对于支持HTML5的浏览器,实现JS的异步加载只须要在script元素中加上async属性,为了兼容老版本的IE还需加上defer属性;对于不支持HTML5的浏览器(IE能够用defer实现),能够采用以上几种方法实现。原理基本上都是向DOM中写入script或者经过eval函数执行JS代码,你能够把它放在匿名函数中执行,也能够在onload中执行,也能够经过XHR注入实现,也能够建立一个iframe元素,而后在iframe中执行插入JS代码。
有些JS代码在某些状况在须要使用,并非页面初始化的时候就要用到。延迟加载就是为了解决这个问题。将JS切分红许多模块,页面初始化时只加载须要当即执行的JS,而后其它JS的加载延迟到第一次须要用到的时候再加载。相似图片的延迟加载。
JS的加载分为两个部分:下载和执行。异步加载只是解决了下载的问题,可是代码在下载完成后就会当即执行,在执行过程当中浏览器处于阻塞状态,响应不了任何需求。
解决思路:为了解决JS延迟加载的问题,能够利用异步加载缓存起来,但不当即执行,须要的时候在执行。如何进行缓存呢?将JS内容做为Image或者Object对象加载缓存起来,因此不会当即执行,而后在第一次须要的时候在执行。
1:模拟较长的下载时间: 利用thread让其sleep一段时间在执行下载操做。 2:模拟较长的JS代码执行时间 var start = Number(new Date()); while(start + 5000 > Number(new Date())){//执行JS} 这段代码将使JS执行5秒才完成!
JS延迟加载机制(LazyLoad):简单来讲,就是在浏览器滚动到某个位置在触发相关的函数,实现页面元素的加载或者某些动做的执行。如何实现浏览器滚动位置的检测呢?能够经过一个定时器来实现,经过比较某一时刻页面目标节点位置和浏览器滚动条高度来判断是否须要执行函数。
一个LazyLoad例子:
原文:http://www.qiqiboy.com/2010/08/27/js-delayed-loading-mechanism-function-lazyload.html
最近应该你们都发现了,个人博客不少模块都使用了延迟加载技术。这个延迟加载(lazyload)我第一次据说其实仍是不久前,那是在别人博客看到 介绍的一个基于jQuery的图片延迟加载插件。我对这个很感兴趣,其实不少网站也都应用了这个技术,尤为是大型网站,能够有效地减小服务器压力。
我原先以为这个技术挺神秘的,实现起来应该比较困难,而且在网上也并无找到相关的文章或者介绍。可是当我真正决定要去这样作时,却发现其实这个真的挺简单的,原理也容易理解。
lazyload,简单地说,就是在浏览器滚动到某个位置时再出发相关函数,实现页面元素的加载或者某些动做的执行。浏览器的滚动位置检测,能够通 过一个定时器来循环检测,经过比较某一时刻页面目标节点位置和浏览器滚动条高度来判断需不要要执行函数。我将这个方法写成了一个类,我本身博客便是使用我 的这个方法。
(下面代码纯属本身的小试手,若是有不完善地方能够指出)
1 function Lazyload(func, obj) { //obj目标节点对象, func要触发的函数 2 this.func = func; 3 this.obj = obj; 4 this.it = setInterval(function() { //2s检查一次 5 this.checkScroll(); 6 }.bind(this), 2000); //bind为setInterval绑定lazyload对象,不然this会指向window对象 7 } 8 9 Function.prototype.bind = function(obj) { //对象绑定 10 var __method = this; 11 return function() { 12 return __method.apply(obj, arguments); 13 }; 14 } 15 16 Lazyload.prototype.checkScroll = function() { 17 var scrollY = document.body.scrollTop || document.documentElement.scrollTop || window.pageYOffset || 0, //页面滚动条高度 18 seeY = window.innerHeight || document.documentElement.clientHeight, //浏览器可视区域高度 19 func = this.func; 20 if (Math.abs(this.getY() - scrollY) < seeY) { //当目标节点进入可视区域,即页面滚动条高度减去节点距页面顶部距离小于浏览器可视高度 21 clearInterval(this.it); //清除定时器 22 return func(); 23 } 24 } 25 26 Lazyload.prototype.getY = function() { //目标节点距页面顶部高度 27 var obj = this.obj; 28 tp = obj.offsetTop; 29 if (obj.offsetParent) 30 while (obj = obj.offsetParent) tp += obj.offsetTop; 31 return tp; 32 }
关于代码的一些解释都在注释里了。如何使用呢?
使用这个方法要经过new来新建类对象的方法。参数func便是要触发的函数,obj便是页面上某个节点对象,即当此obj节点进入可视区域时(浏 览器滚动到此处)即触发func函数。好比,你要实现当浏览器滚动到页面上id为“test”的节点(b)时触发弹出窗口alert(‘yes’),能够 这么作
1: function a(){alert('yes');}//要出发的函数
2: var b=document.getElementById('tests');//获取页面节点
3: new Lazyload(a,b);//使用new关键字创建对象执行
以上就是我本身的lazyload的所有代码与实现方式了。固然,我相信它还有改进之处。
补充一些其余的东西:
个人代码中定时检测的时间设定是2s,也就是说每两秒去比较一次。若是你浏览器拖动太快,目标节点被很快的一闪而过,极可能此时就恰好没有执行 checkScroll函数,因此也就不会触发动做了。你能够将时间改成更小的值(在第6行),如500ms,能够避免大多数的快速拖动形成的问题。当 然,我以为其实不必,若是某人很快的拖动滚动条,说明他要浏览的东西在页面底部,那么实际上是不必加载他快速拖动过的节点的,因此2s我以为是一个很合理的值。
另一个问题是个人代码是只触发出如今可视区域中的节点动做,若是你想要实如今当前浏览的页面以上部分的节点所有要加载,那么能够将代码的第20行去掉Math.abs便可。若是要实如今滚动到距目标节点必定高度时就开始触发函数,那么可将20行的seeY改成seeY+200(200即为设定的距离高度)。
关于lazyload的一个具体的实现,就还拿图片的延迟加载来讲吧。你能够将图片的地址都改为一个默认的小图片(这个能够在后台用php直接修改 输出,也能够用js实现),将正真的图片地址存到图片的alt属性中(也能够指定一个自定义的属性),而后对每一个图片绑定new lazyload,要触发的动做函数就是将alt中的值赋给src便可。这样,就实现了图片的lazyload(你就能够消灭jQuery,消灭 jQuery的lazyload插件了,也就消灭了那100来K的JS文件)。若是再结合ajax技术,那么也就能够实现如我博客同样的模块延迟加载了。
(本文摘自网友的微博组合而成,并非我本人所写。若有冒犯,敬请谅解并告知!)