JavaScript同步、异步与延迟加载详解

作者:白帽猫

时间:2026-10-23

浏览:491

本文详细解析JavaScript的三种加载模式:同步阻塞、异步非阻塞及延迟加载。重点介绍HTML5的async与defer属性用法,以及Script DOM Element、XHR注入、动态创建iframe等异步实现技巧,并探讨通过缓存机制解决JS执行阻塞问题的延迟加载方案,帮助开发者优化页面性能。

文章目录

本文介绍了JavaScript中同步、异步及延迟加载的具体实现方式,供读者参考学习。

同步加载机制与优化

一、同步加载

这是日常开发中最常见的加载方式。

<script src="http://yourdomain.com/script.js"></script>

同步模式也称为阻塞模式,它会暂停浏览器的后续处理与解析,直到当前脚本加载并执行完毕。虽然默认同步执行能确保顺序安全,但如果脚本中包含document.write输出、DOM修改或重定向等操作,会导致页面渲染堵塞。因此,通常建议将<script>标签放置在<body>标签结束前,以尽量减少对页面初始渲染的阻塞影响。

异步加载的多种实现方式

二、异步加载

异步加载(非阻塞加载)允许浏览器在下载和执行JavaScript的同时,继续解析后续页面内容。主要包含以下三种实现方式。

方法一:Script DOM Element


(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均采用了这种异步加载代码。


(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事件中执行额外的渲染工作,这仍可能导致部分页面初始化处理被阻塞。

方法二:在onload事件中异步加载


(function(){
 if(window.attachEvent){
 window.attachEvent("load", 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事件触发的问题。

注:需区分DOMContentLoaded与load事件。前者在document解析完成后触发,此时DOM元素可用但图片、视频等资源可能未加载完毕,作用类似于jQuery的ready事件;后者则在页面所有资源(包括图片、视频等)全部加载完毕后触发。

方法三:其他异步加载技巧

利用JavaScript的动态特性,还存在多种异步加载方案:XHR Injection(XHR注入)、XHR Eval、Script In Iframe、Script defer属性以及document.write(script tag)。

XHR Injection(XHR注入):通过XMLHttpRequest获取JavaScript代码,随后创建script元素并插入DOM结构。在ajax请求成功后,将返回的responseText赋值给script.text属性。


 //获取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 Eval:与XHR Injection不同,它直接将responseText放入eval()函数中执行。


//获取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());
  });
 }
 }

Script In Iframe:在父窗口中插入一个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();

GMail Mobile方案:业内将JS内容置于注释中以避免立即执行,在需要时获取script中的text内容并去除注释,随后调用eval()执行。


 <script type="text/javascript"> 
 /* 
 var ... 
 */ 
 </script>

HTML5 async与defer属性详解

HTML5新属性:async和defer defer属性:自IE4.0起已存在。defer声明脚本中不包含document.write或DOM修改。浏览器会并行下载其他带有defer属性的script,且不阻塞页面后续处理。注意:所有defer脚本必须保证按顺序执行。

    <script type="text/javascript" defer></script>

async属性:HTML5新增属性。脚本将在下载完成后尽快执行,作用与defer类似,但不能保证脚本按顺序执行。它们将在onload事件之前完成。

    <script type="text/javascript" defer></script>

Firefox 3.6、Opera 10.5、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元素并在其中执行插入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):简而言之,即在浏览器滚动到特定位置时触发相关函数,实现页面元素加载或某些动作执行。如何实现浏览器滚动位置检测?可通过定时器实现,比较某一时刻页面目标节点位置与浏览器滚动条高度,以判断是否需执行函数。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持本站。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章