一,加载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属性来完成!