`
yiminghe
  • 浏览: 1460278 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

资源的异步动态加载问题

阅读更多

1.预加载图片

 

图片除了可以用创建 img dom 节点的方法加载外,还可以用常见的缓存图片方法,Image对象加载,基本上创建出来的img dom node 和 new Image 对象 一样的,(除了没有实体标签不可以显示出来) ,但是 ie 对于从缓存中加载的图片触发事件有区别 (和事件添加位置有区别) ,详见代码。特别注意判断缓存加载,有的浏览器(ie chrome)缓存不触发load事件,应该判断img.complete。

 

2.动态加载 script

 

script 不像图片有专有的 javascript 对象,只能通过创建标签的方式加载script,但是 ie 和其他浏览器有区别,其他浏览器可以判断 load 事件 ,像图片一样,而 ie 不会触发这个事件,只能通过判断 readystate 的方式 来解决。

 

ps:

 

动态创建标签时,firefox当设置src时并不会立即开始下载脚本,只有将脚本标签 append 到 document 时,才开始下载,而image则在img src设置后立即开始下载,即使还没有被 append 到 document。

 

ie 浏览器判断 onreadystatechange 比较怪异,loaded 和 complete 每个并不确定出现,所以最好两者都判断,但为了防止执行逻辑两次,脚本加载后将 onreadystatechange 置空为好 ,如:

 

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.readyState == "complete"){
                script.onreadystatechange = null;
                callback();
            }
        };
    } else {  //Others
        script.onload = function(){
            callback();
        };
    }

    script.src = url;
    document.body.appendChild(script);
}
 

 

updated : 2011-05-11

 

 

在 ie6,7,8 下有缓存情况,嵌套的动态加载会出问题

 

loadScript("right.js",function(){
  
  loadScript("right2.js",function(){alert(2);});

});
 

则 alert(2) 也不会执行!

 

 

举个例子From Even Faster Websites :

 

Coupling Asynchronous Scripts ,若一段inline脚本依赖于一个外部脚本文件,而我们用script dom 添加的方式载入外部脚本,因为除了firefox 都不能保证等外部脚本加载完毕再执行内部脚本,所以的采用其他方法。


1.判断script 状态

 

var aExamples = [['couple-normal.php', 'Normal Script Src'],...];
function init() {
	//依赖于menu.js
	EFWS.Menu.createMenu('examplesbtn', aExamples);
}
var domscript = document.createElement('script');

domscript.src = "menu.js";
//opera 会 load 两次,so 设置变量
domscript.onloadDone = false;
//标准方式
domscript.onload = function() {
	domscript.onloadDone = true;
	init();
};

//ie 和 opera ?
domscript.onreadystatechange = function() {
if ( "loaded" === domscript.readyState && ! domscript.onloadDone ) {
	domscript.onloadDone = true;
	init();
}
}
document.getElementsByTagName('head')[0].appendChild(domscript);
 

2.Degrading Script Tags


ejohn 在他的blog中提出了这个优雅而又怪异的方法 ,关键要在外部脚本后加入代码执行外部脚本的 innerHTML

 

如上述的 menu.js,最后添加

 

var scripts = document.getElementsByTagName("script");
var cntr = scripts.length;
while ( cntr ) {
	var curScript = scripts[cntr-1];
	//如果找到它自己
	if ( -1 != curScript.src.indexOf("menu.js") ) {
		//执行依赖它的内部脚本
		eval( curScript.innerHTML );
		break;
	}
	cntr--;
}

 

使用 menu.js 的代码 :

 

<script type="text/javascript">
var aExamples = [['couple-normal.php', 'Normal Script Src'],...];
function init() {
	EFWS.Menu.createMenu('examplesbtn', aExamples);
}
var domscript = document.createElement('script');
domscript.src = "menu.js";

//设置依赖的要执行的代码
if ( -1 != navigator.userAgent.indexOf("Opera") ) {
	domscript.innerHTML = "init();";
}
else {
	domscript.text = "init();";
}
document.getElementsByTagName('head')[0].appendChild(domscript);
</script>
 

3.其他方法:

比如 setTimeout polling,外部脚本硬编码callback,window.onload 再执行内部脚本,都不如上述两个方法。

 

注意: 异步新载入的script标签即使后来删除掉,其里面定义的函数,变量仍然存在于window 中,所以请不要将这种方式滥用,建议只用于跨域的数据交换

 

2.1 script 加载完毕的另一种判断,反向通知

 

2009-07-03 修正

 

但是 script 和 img不同的是,异步载入的javascript可以反向通知调用方,即调用调用方的代码告知自己已经载入完毕。


Ext.data.ScriptTagProxy


这就需要异步载入的javascript要调用调用方提供的临时的一个全局函数,调用完毕将这个全局函数删除。


如 ScriptTagProxy :

 

window[trans.cb] = undefined;
            try{
                delete window[trans.cb];
            }catch(e){}

 

每次请求都临时产生一个全局函数window[trans.cb]来等待异步载入的javascript来执行。


注意:


        try{
                delete window[trans.cb];
            }catch(e){}

 


由于 IE不能清除window上的属性,会抛出无法操作的异常 ,这里捕捉一下,我认为不清除也无所谓,设成 undefined 就可以垃圾回收了呀。

 

ps:关于delete的详细解释 :Understanding delete

 

2.2 关于 window 属性清除问题还要注意:


如果不是显式设置 window.property,那么property ie ff都无法清除,但是ff不会报错的。如:

 

	var test=function (){
		alert(1);
	};
	
	/*或者
	
	function test(){
		alert(1);
	};
	*/
	
	
	delete window["test"];
	delete test;
	alert(window["test"]);
 

 

2.3 多个异步脚本加载顺序问题

 

通过同时动态创建多个脚本节点来异步加载脚本,ie与firefox之间也有差异,ie只要节点src设置即可开始下载,firefox则要将节点插入到文档才开始下载,虽然它们都会同时开始下载,但是完成的通知时间不同,firefox的通知时间与插入文档的顺序相同

(注:firefox 3.6 script引入async属性 ,默认为false,若设为true则和ie表现一致,firefox 4.0 起参数默认 true )

,而ie则哪个先下载完先通知哪个:(可使用fiddler模拟拨号上网测试)

 

<script type="text/javascript" src="http://yui.yahooapis.com/3.1.0/build/yui/yui-min.js"></script>
<script>
YUI({
    filter: "DEBUG"
}).use("*", function (Y) {
    var script1 = document.createElement("script");
    script1.setAttribute("src", "http://yui.yahooapis.com/combo?3.1.0/build/oop/oop-min.js&3.1.0/build/dom/dom-min.js&3.1.0/build/pluginhost/pluginhost-min.js&3.1.0/build/event-custom/event-custom-min.js&3.1.0/build/node/node-min.js&3.1.0/build/event/event-min.js&3.1.0/build/attribute/attribute-base-min.js&3.1.0/build/base/base-min.js&");
    console.log(script1.async);
    //script1.async=true;
    document.getElementsByTagName("head")[0].appendChild(script1);
    script1.onload = function () {
        console.log("script1 load");
    };
    script1.onreadystatechange = function () {
        var rs = this.readyState;
        if ("loaded" === rs || "complete" === rs) {
            // Y.log(id + " onreadstatechange " + url, "info", "get");
            script1.onreadystatechange = null;
            console.log("script1 onreadystatechange");
        }
    };
    var script2 = document.createElement("script");
    script2.setAttribute("src", "http://yui.yahooapis.com/combo?3.1.0/build/dom/dom-min.js");
    document.getElementsByTagName("head")[0].appendChild(script2);
    script2.onload = function () {
        console.log("script2 load");
    };
    script2.onreadystatechange = function () {
        var rs = this.readyState;
        if ("loaded" === rs || "complete" === rs) {
            // Y.log(id + " onreadstatechange " + url, "info", "get");
            script2.onreadystatechange = null;
            console.log("script2 onreadystatechange");
        }
    };
});
</script>

 

PS : defer 和 async 作用并不一样,图解:  

 

 

 

 

 

2.4 动态添加脚本的同步与异步问题

 

当动态添加脚本时,不同的应用场景可能需要不同的脚本处理顺序,注意:当动态添加内联脚本时为同步,添加外部脚本时为异步:

 

同步:

 

     var script1 = document.createElement("script");
     document.getElementsByTagName("head")[0].appendChild(script1);
     script1.text="alert('script1 loaded');";
     alert(1);

   输出:script1 loaded 1

 

异步:

 

var script2 = document.createElement("script");
    script2.setAttribute("src", "test.js");
    document.getElementsByTagName("head")[0].appendChild(script2);
    script2.onload = function () {
        alert("script2 load");
    };
    script2.onreadystatechange = function () {
        var rs = this.readyState;
        if ("loaded" === rs || "complete" === rs) {
            // Y.log(id + " onreadstatechange " + url, "info", "get");
            script2.onreadystatechange = null;
            alert("script2 load");
        }
    };
    alert(2);

    输出:2 test.js loaded script2 loaded

 

test.js:

 

alert("test.js loaded");

 


2.5 出错判断

 

2011-03-03 note :

 

当动态加载的脚本发生错误(比如服务器返回 404 )时,ie和标准浏览器(firefox,chrome)表现也有区别:

 

1. ie 在 404 情况下仍然会使得脚本的 readyState 变为  loaded 或者 complete,所以如果想知道是否出错,估计得修改业务逻辑

 

2. firefox,chrome 在服务器出错时会触发 error 事件,通过监听 error 以及 load 可立即知道成功与否。

 

 

 

总体测试代码:

 

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
	<head>
    <title>XXX</title>
    <script src="ext-base.js" type="text/javascript"></script>
    <script src="ext-core.js" type="text/javascript"></script>
  </head>
  <body>
  	
  	<input type="button" value="load image" id="test" />
  	<input type="button" value="load script" id="test2" />
  	
  	
<script type="text/javascript">
Ext.DomHelper.useDom = true;
Ext.onReady(function () {
    Ext.get('test2').on('click', function (evt) {
        //即使移除script标签,函数依旧在内存中存在
        if (Ext.get('st')) Ext.get('st').remove();
        //so assign to undefined explicitly
        window['writeflashhtml'] = undefined;
        //sorry can not delete 在window声明的函数
        //delete window['writeflashhtml'];
        //alert(typeof window['writeflashhtml']);		
        var el = Ext.DomHelper.append(Ext.getBody(), {
            tag: 'script',
            id: 'st' //,
            //src:"http://img.12580zgz.com/js/flashobj.js"				
        },
        true);
        el.dom.src = "http://img.12580zgz.com/js/flashobj.js";
        /*
  						ie 不会触发load 事件,ie 以外浏览器 会
  						*/
        el.on('load', function () {
            alert("non-ie createElement onload:" + writeflashhtml);
        });
        /*
	  				 ie 这样判断是否加载完毕
	  				*/
        el.dom.onreadystatechange = function () {
            if ( //this.readyState == 4 
            this.readyState == 'complete' || this.readyState == 'loaded') {
                alert(this.readyState + " : ie createElement onload:" + writeflashhtml);
            }
            el.dom.onreadystatechange = null;
        };
        evt.stopEvent();
    });
    Ext.get('test').on('click', function (evt) {
        var im = new Image();
        /*
  					将onload 移到前面 ,ie 则每次都会触发onload 事件
  					im.onload=function(){
  						alert("new Image load:"+this.width);
  					}
  					*/
        im.src = "http://img.12580zgz.com/images/logo-w3c.gif";
        /*
  						将onload 放在后面,则 ie取缓存图片时不会触发onload事件*/
        //	im.addListener("load",function(){});
        im.onload = function () {
            alert("new Image load:" + this.width);
        }
        //图片来自缓存触发,或者到这已经图片载入,所有浏览器使用
        if (im.complete) {
            alert("new Image complete:" + im.width);
        }
        var el = Ext.DomHelper.append(Ext.getBody(), {
            tag: 'img'
        },
        true);
        /*
		  			将onload 移到前面 ,ie 则每次都会触发onload 事件
		  				el.on('load',function(){
		  					alert("createElement onload:"+this.dom.width);
		  				});
		  				
		  			*/
        el.dom.src = "http://img.12580zgz.com/images/logo-w3c.gif";
        /*
  						将onload 放在后面,则 ie取缓存图片时不会触发onload事件*/
        el.on('load', function () {
            alert("createElement onload:" + this.dom.width);
        });
        //来自缓存触发,或者到这已经图片载入,所有浏览器使用
        if (el.dom.complete) {
            alert("createElement complete:" + el.dom.width);
        }
        evt.stopEvent();
    });
});
</script>
</body>
</html>
   


ps: Iframe与窗口文档 相关操作

 


javascript 与 iframe

页面加载判断与onload 研究

 

 

 

参考文章:

 

Loading JavaScript without blocking

 

Understanding delete

  • 大小: 22.4 KB
分享到:
评论
1 楼 haiyang5210 2010-11-19  
/*********************动态载入JS Satrt************************/
function loadJS (url, onload) {
var domscript = document.createElement('script');
domscript.src = url;
if ( onload ) {
domscript.onload = onload;
domscript.onreadystatechange = function() {alert(life_assist);
if (domscript.readyState == "loaded"||domscript.readyState == "complete" ) {
domscript.onload();
}
}
}
document.getElementsByTagName('head')[0].appendChild(domscript);
}
/*********************动态载入JS Satrt************************/
/*******绑定到window.onload且不覆盖已有方法 Start*************/
if (document.all) {  
    window.attachEvent('onload', bodyloadready);  
}  
else {  
    window.addEventListener('load', bodyloadready, false);  

/*******绑定到window.onload且不覆盖已有方法 Start*************/
function bodyloadready(){
loadJS('htdocs/js/life_assist.js',function (){
alert(1);
});
};

相关推荐

    js 异步加载js, css文件

    当项目js(css)文件使用越来越多,js 文件的加载也成了性能上的一个问题,此资源能够在页面全部加载完成后异步加载js等资源文件,它可以顺序加载资源列表,也可以并发加载资源列表,它包含一个方法调用接口:...

    资源异步加载和缓存

    在IT行业中,资源异步加载和缓存是提高应用程序性能和用户体验的关键技术。尤其是在移动开发领域,由于设备硬件限制和网络环境的不稳定性,优化资源加载策略显得尤为重要。本篇文章将详细探讨异步加载的不同实现方式...

    Unity3D多个异步加载场景资源 简洁漂亮的加载画面

    2. `AssetBundle`:AssetBundle是Unity3D中另一种资源异步加载的方式。它可以将游戏的模型、纹理、音频等资源打包成独立的文件,然后在需要时按需加载。这种方式更加灵活,可以实现资源热更新和动态下载。创建...

    JS 动态树 异步加载树 xloadtree WebFXLoadTree

    标题 "JS 动态树 异步加载树 xloadtree WebFXLoadTree" 指的是在JavaScript中实现的一种特定的树形结构组件,它具备动态加载和异步数据获取的功能。这种技术常用于构建大型数据集的层级展示,如文件系统、组织结构或...

    Unity AssetBundle 资源同步/异步加载管理器

    基于Unity封装的AB包资源管理器,实现同步加载/异步加载AB包以及依赖包,详细可以到我的博客查看文章,里面也有完整代码。

    动态加载资源

    这个过程可能涉及到文件I/O操作、内存管理以及异步加载机制,以确保加载过程不会阻塞用户界面。 UniFBX 2_2.3.1.unitypackage文件名暗示这是一个Unity引擎的资源包,可能包含了用于演示动态加载功能的3D模型或场景...

    ExtJS构造动态异步加载

    本文将深入探讨如何使用ExtJS构建动态异步加载的树形结构,结合AJAX技术实现JSON数据的高效传输。 首先,我们要理解什么是动态异步加载。在传统的网页开发中,如果一次性加载所有数据,可能会导致页面加载速度慢,...

    Unity3D实现资源动态加载Demo

    2. 异步加载:为了不影响游戏流畅性,通常使用异步加载方式。当加载完成时,Unity会触发回调函数,此时可以进一步处理加载的资源。 3. 加载资源:加载完AssetBundle后,使用`AssetBundle.LoadAssetAsync`方法加载...

    图片异步加载,照片墙,异步加载listview图片2

    在Android应用开发中,图片异步加载是一种常见的优化技术,特别是在构建类似照片墙或ListView这样的大量图片展示场景中。这个话题主要关注如何有效地处理图片资源,避免UI阻塞,提高用户体验。以下是对"图片异步加载...

    js异步加载代码

    异步加载则能避免这一问题,使页面可以先呈现基本结构,然后在后台加载和执行脚本。 在"asyLoad.js"这个文件中,我们可以预期它包含了一段用于实现JavaScript异步加载的代码。下面将详细介绍异步加载的原理和常见...

    JS异步加载图片

    在JavaScript(JS)中,异步加载图片是一种优化网页性能的技术。它允许浏览器在不影响页面主要功能的情况下,按需或后台加载非关键资源,如图片。这样可以减少页面初次加载的时间,提高用户体验,特别是对于含有大量...

    Unity3D协同函数与异步加载功能实战

    3. **异步加载资源**:使用`LoadAsync`方法异步加载资源,并通过回调函数处理加载完成后的逻辑。 ```csharp AssetBundleRequest request = assetBundle.LoadAssetAsync("assetname"); request.completed += ...

    unity通过协程实现异步下载数据并加载

    首先,我们需要将下载的数据转换为AssetBundle,然后使用`AssetBundle.LoadAsync`方法异步加载资源。以下是一个简单的示例: ```csharp // 假设已下载的数据存储在receivedData中 AssetBundle assetBundle = ...

    Android异步加载文字

    在Android开发中,异步加载是一项至关重要的技术,主要用于解决UI线程阻塞问题,提升应用性能和用户体验。Android Studio是Google官方推出的Android应用程序开发集成开发环境(IDE),它为开发者提供了丰富的工具集...

    cocos2d-x多线程异步加载资源

    通过以上步骤,你可以在Cocos2d-x游戏中实现高效且流畅的资源异步加载,提升游戏的运行效率和用户体验。记住,合理地管理线程和资源是优化游戏性能的关键。同时,还要关注线程安全问题,避免因数据竞争引发的错误。

    JS异步加载库

    2. 动态`&lt;script&gt;`元素:通过JavaScript动态创建`&lt;script&gt;`元素并插入到DOM中,可以实现异步加载。这种方式允许在运行时根据需要加载脚本,避免一次性加载所有资源。 3. Promises 和 Async/Await:利用Promise和...

    ListView异步加载图片

    为了解决这个问题,我们需要实现ListView的异步加载图片功能。本篇文章将详细介绍如何通过软引用缓存图片,实现高效、流畅的异步加载机制。 一、异步加载原理 异步加载的基本思想是将耗时的操作(如网络请求和图片...

    QML中动态创建组件(同步/异步)的方法

    异步加载组件是处理大组件或网络加载资源时的最佳实践。对于`Loader`,你可以通过监听其`status`属性的变化来实现异步加载: ```qml Loader { id: myLoader source: "MyComponent.qml" onStatusChanged: { if ...

    jquery 异步加载页面

    在Web开发中,异步加载页面是一种常见的优化技术,它允许我们在不刷新整个页面的情况下动态地加载新的内容。jQuery,一个强大的JavaScript库,提供了丰富的API来实现这一功能,使得网页交互更加流畅,用户体验更佳。...

Global site tag (gtag.js) - Google Analytics