张经纬的博客 - 中隐留司官

动态加载js

本文让js动态加载,这样原本串行加载的js就变成并行了。

先看两个demo

demo_00:http://www.zhangjingwei.com/loadscript/demo_00/

demo_00

再看demo_01:http://www.zhangjingwei.com/loadscript/demo_01/

demo_01

很明显,demo_01中,js异步加载,没有阻挡图片的下载

让我们看看他是如何实现的。

var loadscript =
{
	$$:function(id){return document.getElementById(id)},
	tag:function(element){return document.getElementsByTagName(element)},
	ce:function(element){return document.createElement(element)},
	js:function(url)
	{
		s = loadscript.ce('script');
		s.type = "text/javascript";
		s.onreadystatechange = ready;
		s.onerror = s.onload = complete;
		s.src = url;
		loadscript.tag('head')[0].appendChild(s);
		function ready(){}
		function complete(){}
	}
}

代码很简单,但我们忽略了一个很重要的问题。
虽然JS是异步加载了,但他们下载的顺序却不确定,万一B先与A加载,并且B调用了A内的函数呢?
这种情况在IE里经常出现。
2009-08-17_004534

2->1->3->4->6->5 就是这些js的加载顺序,现在我们想让他们变成1->2->3->4->5->6。怎么办?

我们增加一个回调,让他在第一个加载完成后再加载第二个。

var loadscript =
{
	$$:function(id){return document.getElementById(id)},
	tag:function(element){return document.getElementsByTagName(element)},
	ce:function(element){return document.createElement(element)},
	js:function(url,callback)
	{
		s = loadscript.ce('script');
		s.type = "text/javascript";
		s.onreadystatechange = ready;
		s.onerror = s.onload = callback;
		s.src = url;
		loadscript.tag('head')[0].appendChild(s);
		function ready(){
			if (s.readyState == 'loaded' || s.readyState == 'complete') {
				callback();
			}
		};
	}
}

这样写得到了我们想要的结果。
demo_02:http://www.zhangjingwei.com/loadscript/demo_02/

02

可是加载它的方法很臃肿。

loadscript.js('js1.js',function(){
  loadscript.js('js2.js',function(){
	loadscript.js('js3.js',function(){
	  loadscript.js('js4.js',function(){
		loadscript.js('js5.js',function(){
		  loadscript.js('js6.js',function(){
		  });
		});
	  });
	});
  });
});

改进一下 (同步加载)
demo_03:http://www.zhangjingwei.com/loadscript/demo_03/

var loadscript =
{
	$$:function(id){return document.getElementById(id)},
	tag:function(element){return document.getElementsByTagName(element)},
	ce:function(element){return document.createElement(element)},
	ls:function (url){
		var req = this.createXmlHttp();
		req.open('GET',url, false);
		req.send(null);
		//alert (req.status);
		if (req.status == 200 || req.status == 0) {
			window.eval(req.responseText);
		};
	},
	createXmlHttp: function (){
		var xmlHttp;
		if (window.ActiveXObject) {
			xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
		}else if (window.XMLHttpRequest){
			xmlHttp = new XMLHttpRequest();
		}
		return xmlHttp;
	}
}

看看效果如何。
03

js确实是串行下载了,可问题又来了,因为是同步加载又将图片的加载阻止了。

鱼与熊掌不可兼得么?有知道的朋友还请留言赐教。

干活去了! :)

加入书签:
  • QQ书签
  • 豆瓣
  • 豆瓣九点
  • Haohao
  • RSS
  • email

原文链接|

目前 共有 7 条评论 ,点此发表评论

  1. suntear

    八月 18th, 2009 @ 10:18

    这个方法不错!多个css文件岂不是也可以这样了?以前听说过有人在服务器端高贵实现这种效果。

    回复

  2. Asins

    八月 18th, 2009 @ 17:57

    之前接触过LABjs这个小小的东西,作用就是加载JS文件,很方便,使用方法也比你这个要好些。

    回复

  3. 张经纬

    八月 19th, 2009 @ 09:35

    @suntear
    css也可以这样,其实这个用了好多年了,加载css我个人觉得没有多大意义,首先css并不会很大,另外css并不会组织并行下载,最后动态载入样式的时候,浏览器要重新渲染页面,其实挺耗费资源的。
    不过js就要另外说了。

    @Asins
    我只是一个demo,抛砖引玉,并不完善,千万不能拿来就用,比如说我没有判断加载的是否重复等等。 :)

    回复

  4. onion83

    八月 20th, 2009 @ 18:06

    思路很好,不过写起来有点浪费代码
    loadscript.ls(‘js1.js’);
    loadscript.ls(‘js2.js’);
    loadscript.ls(‘js3.js’);
    loadscript.ls(‘js4.js’);
    loadscript.ls(‘js5.js’);
    loadscript.ls(‘js6.js’);

    做成一个干净的接口 loadscript.ls(‘js1′,’js2′,’js ….’); 多好

    php 有一个叫auto_load的机制,核心的思想是用到的时候再加载.

    例如 我调用的函数 package_a() ;能不能不考虑不需要加载那个js,反正只管调用.如果package_a() 又调用了 package_b() 那也自动再加载相关的文件.呵呵,这个就上升到一个编程的框架和包管理的级别了~可以多往这个方向思考一下.

    回复

  5. 颜小城

    八月 28th, 2009 @ 18:00

    我不懂。

    回复

  6. 小毛

    八月 30th, 2009 @ 18:29

    这个问题不是你可以考虑的,应该由浏览器开发人员来处理.

    回复

    张经纬 reply on 八月 31st, 2009 09:13:

    @小毛
    ….大汗啊。

    回复