javascript文件加载过程中产生的依赖问题

一,加载JS文件产生的问题

 <script src="1.js"></script>
 <script src="2.js"></script>
 <script src="3.js"></script>
 <script src="4.js"></script>
 <script src="5.js"></script>
 <script src="6.js"></script><span style="font-family: Arial, Helvetica, sans-serif;">/*依次等待下载并执行,前面的js阻塞后面的js下载执行*/</span>
note:这种加载js文件可能产生的问题:

(1)当js引擎遇到<script>标签的时候,页面必须停下来等待下载和执行代码(注意:是等待下载和执行),然后才能处理页面剩余部分。因为大多数浏览器使用单进程处理UI更新和javascript运行等多个任务,并且同一个时间只能处理一个任务。当javascript运行时候而其它事情不能被浏览器处理时,javascript运行了多长时间,那么响应用户的行为就要等待多长时间!不论实际的javascript代码是内联的还是包含在一个不相干的外部文件中,页面下载和解析过程必须停下,等待脚本完成相应的处理才能继续,这是页面生命周期中必不可少的部分,因为脚本可能在运行过程中修改页面内容。
(2)浏览器在解析body标签之前不会渲染页面的任何部分,如果把这个代码放在页面顶部,那么就会导致一个可以察觉的延迟,在页面打开之前显示为一片空白,此时用户不能阅读也不能与页面交互。

(3)一般我们希望script标签在下载外部资源的时候必须阻塞其它的script标签,不幸的是,javascript的下载仍要阻塞其它资源(比如图片)的下载过程,即使脚本之间的下载过程互不阻塞,页面仍要等到所有的javascript代码下载并且执行完之后才能继续,因此在浏览器允许并行下载挺高性能之后,脚本阻塞仍然是一个问题。

推荐方法1:

 script标签放在页面的底部,紧靠body关闭标签的上面,这面可以保证页面在脚本运行之前完成解析。这时候虽然脚本下载之间互相阻塞,不过页面已经下载完成并且显示在用户面前了;同时,可以合并多个javascript脚本减少HTTP次数的同时,也可以减少多个script标签互相阻塞从而影响页面加载的情况。

推荐方法2:

脚本延迟执行。defer属性表明脚本不修改DOM,因此代码可以稍后执行,但是只适用于IE4+和FF3.5+。在其它浏览器中defer被忽略,又会出现阻塞。defer会在script标签时候下载,但是会在DOM加载完成,也就是load时间之前执行。在defer的javascript下载时候,他不会组设浏览器其它的处理过程,所以这些文件可以和页面的其它资源一起并行下载!

<html>
 <head>
     <title>写博客</title>    
        <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
</head>

  <body>
       <script defer='defer'> alert("defer");</script>
	   <script>alert("script");</script>
       <script>
         window.οnlοad=function(){alert("load");};
	   </script>
    </body>
</html>
注意:在chrome47中弹出顺序为defer,script,load也就说出现阻塞,前面的脚本阻塞了后面的脚本的执行。但是在IE8中弹出的顺序是script,defer,load。 也就说defer的脚本会在load事件调用之前被触发!
上面这种方法只是适用于FF和IE,对于这种情况我们可以采用动态脚本加载技术来完成脚本延迟执行,动 态脚本加载技术是非阻塞的javascript下载中最常用的模式,可以跨浏览器。

         var script=document.createElement("script");
		 script.type="text/javascript";
		 script.src="1.js";
		 document.getElementsByTagName("head")[0].appendChild(script);/*在这之前不会下载脚本*/
注意:这种方式在元素被添加到页面之后立刻开始下载,这样无论在何处启动下载, 文件的下载和运行都不会阻塞其它的页面处理过程,甚至可以将代码放在head部分而不会对其余部分的脚本产生影响,下载文件的HTTP链接的情况除外。那么怎么知道脚本下载完成了呢,我们可以跟踪具体的事件:

  /*页面使用动态脚本加载时候,返回的代码会立即执行,但是FF和Opera会等待此前所有的动态脚本节点执行完毕*/
         var script=document.createElement("script");
		 script.type="text/javascript";
		 /*我们需要检测脚本的加载情况*/
		 /*FF,Opera,Chrome,Safari3+会在script节点接受完成之后发出load事件*/
		 script.οnlοad=function()
		 {
		 
		 }
		 /*IE不支持load,用readystatechange事件*/
		 script.onreadystatechange=function()
		 {
		   if(script.readyState=='loaded'||script.onreadyState=='complele')
		   {
		      /*防止触发了loaded有触发complete,或者触发了complte又触发loaded*/
		      script.onreadystatechange=null;
			  /*其它代码*/
		   }
		 }
		 /*注意:对于图片来说要先指定事件处理程序,因为指定src就会开始下载,但是script来说要添加到DOM中才会开始下载,因此顺序不那么重要了*/
		 script.src="1.js";
		 document.getElementsByTagName("head")[0].appendChild(script);
但是,这种方式没有重用性,所以我们写了标准的函数形式,如下:

function loadScript(url,callback)
   {
          var script=document.createElement("script");
		 script.type="text/javascript";
		 if(script.readyState)//IE浏览器
		 {
		    script.onreadystatechange=function()
			 {
			   if(script.readyState=='loaded'||script.onreadyState=='complele')
			   {
				  script.onreadystatechange=null;
				  callback();
			   }
			 }
		 }else//非IE
		 {
		    script.οnlοad=function()
			 {
			     callback();
			 }
		 }
	  script.src=url;
	 document.getElementsByTagName("head")[0].appendChild(script);
   }
note:这里要注意,这种方式不保证文件加载的顺序,在所有的浏览器中只有FF和Opera保证脚本按照指定的顺序执行,其它浏览器将按照服务器返回次序下载并执行不同的代码文件。于是可以按照下面的方式来加载文件:

loadScript("1.js",function()
{
 loadScript("2.js",function()
 {
   loadScript("3.js");
 });
});
这种方式虽然能够保证文件加载的顺序,但是如果要加载的文件很多那么效率还是有问题。这时候可以按照顺序将文件拼接成为一个大的文件, 独立的文件可以一次性下载所有的代码,由于这里是异步的,因此使用一个大文件没有什么损失。也可以用后面介绍的requireJS的内容:

我们来看一下jQuery中script跨域加载脚本的时候的代码:

// Bind script tag hack transport
jQuery.ajaxTransport( "script", function(s) {
	// This transport only deals with cross domain requests
	if ( s.crossDomain ) {
		var script,
			head = document.head || jQuery("head")[0] || document.documentElement;
		return {
			send: function( _, callback ) {
				//通过动态创建script来完成,同时是异步的,而且编码就是自己在options中指定的scriptCharset!
				script = document.createElement("script");
				script.async = true;
				if ( s.scriptCharset ) {
					script.charset = s.scriptCharset;
				}
				//指定script的src和回调之间没有先后关系限制,如果是图片必须首先指定回调才行!
				script.src = s.url;
				// Attach handlers for all browsers
				script.onload = script.onreadystatechange = function( _, isAbort ) {
					if ( isAbort || !script.readyState || /loaded|complete/.test( script.readyState ) ) {
						//如果已经完成请求,那么把onload和onreadystatechange清空,防止内存泄漏!
						// Handle memory leak in IE
						script.onload = script.onreadystatechange = null;
						//移除这个脚本
						// Remove the script
						if ( script.parentNode ) {
							script.parentNode.removeChild( script );
						}
						// Dereference the script
						script = null;
                        //加载完成进行回调!
						// Callback if not abort
						if ( !isAbort ) {
							callback( 200, "success" );
						}
					}
				};
				// Circumvent IE6 bugs with base elements (#2709 and #4378) by prepending
				// Use native DOM manipulation to avoid our domManip AJAX trickery
				//把scipt元素添加到head最前面的!
				head.insertBefore( script, head.firstChild );
			},
			abort: function() {
				if ( script ) {
					script.onload( undefined, true );//这时候就不会回调callback函数,因为isAbort为true!
				}
			}
		};
	}
});
note:该方法处理了IE存在的可能的内存泄漏问题。

二,加载行内script内容

 window.οnlοad=function()
	   {
		   function loadScript(code)
		   {
			 var script=document.createElement("script");
				 script.type="text/javascript";
				 try
				 {
				  script.appendChild(document.createTextNode(code));//对于非IE浏览器的调用方式
				 }
				 catch (ex)
				 {
			script.text=code;//IE将script标签看作特殊的元素,不允许DOM访问其子节点,但是可以用script元素的text属性来指定javasctipt代码!
				 }
				document.body.appendChild(script);
			  }
			 loadScript("alert('hello')");
		  }
note:这种方式加载的代码会在全局作用域中执行,而且当脚本执行后立即可用,实际上 ,这种方式执行的代码何在全局作用域中把相同的字符串传递给eval函数是一样的!
三,用 requireJS来解决模块依赖问题以及js异步加载避免页面失去相应
       因为上面加载JS的方式会使得页面失去响应的时间较长(因为js会出现阻塞),同时如果要严格保证js的加载顺序的时候就需要用异步加载的形式来完成了!

四,用XHR脚本注入来加载JS代码(非阻塞式)

   var xhr=new XMLHttpRequest();
   xhr.open("get","1.js",true);
   xhr.onreadystatechange=function()
   {
     if(xhr.readyState==4)
	 {
	   if(xhr.status>=200&&xhr.status<300||xhr.staus==304)
	   {
	      	 var script=document.createElement("script");
				 script.type="text/javascript";
				 script.text=xhr.responseText;
                document.body.appendChild(script);			   
		}
	 }
   }
  xhr.send(null);
note:一旦新的script被添加到文档中,代码就会被执行,并且准备使用。该方法的优点是: 可以下载不立即执行的代码,因为代码返回在script标签外,所以下载后不会自动执行,这使得可以推迟执行,直到一切都准备好了(如点击后才下载,模拟requireJS);另外一个优点是,同样的代码在所有浏览器中不会异常。缺点:javascript文件必须和页面在同一个于内。
五,动态样式的加载问题

function loadStyle(css)
{
  var style=document.createElement("style");
  style.type="text/css";
  try
  {
	style.appendChild(document.createTextNode(css));
  }
  catch (ex)
  {
    style.stylesheet.cssText=css;//IE将style看作特殊的节点,通过styleSheet属性的cssText访问内容
  }
  var head=document.getElementsByTagName("head")[0];
  head.appendChild(style);
}
note:加载外部样式的过程是异步的,也就是加载样式和执行js代码的过程没有固定的次序。
六,javascript加载导致的 阻塞问题

外部脚本阻塞:阻塞脚本后面内容的下载和呈现

内联脚本阻塞:阻塞后面的内容下载和整个页面的呈现(不同浏览器有差异)

嵌入js导致css阻塞加载:发生条件是内联js前面有外部css的情况(外部的js前面有外部css也会阻塞)  根本原因:因为浏览器会维持html中css和js的顺序,样式表必须在嵌入的JS执行前先加载、解析完(因为CSSOM必须是稳定的,而js可能会修改CSSOM)。而嵌入的JS会阻塞后面的资源加载,所以就会出现上面CSS阻塞下载的情况。解决方法:把js放在底部,虽然会阻塞所有呈现但是不会阻塞页面资源下载;如果放在head中要放在css前面;使用defer;当调用时间较长时候用setTimeout!

总结:

(1)IE访问javascript内容用text属性,而如果是css那么就用style.styleSheet.cssText属性来完成!

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值