BearRui(AK-47)
花开有时,错过了一日便错过了一季,就象人生错过了相遇,就不再找寻到美丽的相聚
随笔- 39  文章- 2  评论- 1288 
博客园  首页  新随笔  联系  管理  订阅 订阅
JS 实现完美include

  js为什么需要include?让我们想想这样1个场景,a.js 需要用到1个公用的common.js,当然你可以在用到a.js的页面使用<script src="common.js">,但假设有5个页面用到了a.js,你是不是要写5遍<script。而且要是以后a.js 又需要引用common2.js,你是不是又的修改5个页面了?

 

已有js include的一些问题

    在写这个之前在网上搜索了些资料,发现以前写的include都存在2个问题,这也是include需要解决的比较重要的2个问题。

   1、相对路径的问题:  在a.js中使用include("../js/common.js");  include 函数中肯定是使用相对路径,是相对a.js的路径。而a.js在html中使用<script>嵌入有可能是相对路径,有可能是绝对路径。  include函数如何才能真正确定common.js的绝对路径,或者是相对html的相对路径。网上一些为了解决这个问题,还需要加一些js变量,不方便。  

   2、引用的问题。  网上include函数的实现几乎都是使用下面2种方式插入common.js     

      document.write("<script src='" + .. + "></script>")  

    或者    

      var s = document.createElement("script");    

      s.src = ...;    

      head.insertAfter(s,...);    

    document.write 输出的脚本会在a.js后面加载,而createElement("script")创建的脚本是非阻塞加载。  所以如果在common.js加载完毕之前,a.js中调用了common.js的函数就会报错。

 

实现

     解决上面2个问题,就可以实现js include。  

   第1个问题,我的方法是先获取到a.js在html中的绝对路径(如果是相对路径,就转为绝对路径),然后再把common.js的路径转为绝对路径。  

   第2个问题,采用同步的ajax来请求common.js,这样就不会出现引用问题。

 

  实现代码如下:

 

	// 根据相对路径获取绝对路径
	function getPath(relativePath,absolutePath){
		var reg = new RegExp("\\.\\./","g");
		var uplayCount = 0;		// 相对路径中返回上层的次数。
		var m = relativePath.match(reg);
		if(m) uplayCount = m.length;
		
		var lastIndex = absolutePath.length-1; 
		for(var i=0;i<=uplayCount;i++){
			lastIndex = absolutePath.lastIndexOf("/",lastIndex);
		}
		return absolutePath.substr(0,lastIndex+1) + relativePath.replace(reg,"");
	}	 	
	
	function include(jssrc){
		// 先获取当前a.js的src。a.js中调用include,直接获取最后1个script标签就是a.js的引用。
		var scripts = document.getElementsByTagName("script");
		var lastScript = scripts[scripts.length-1];
		var src = lastScript.src;
		if(src.indexOf("http://")!=0 && src.indexOf("/") !=0){		
			// a.js使用相对路径,先替换成绝对路径
			var url = location.href;
			var index = url.indexOf("?");
			if(index != -1){
				url = url.substring(0, index-1);
			}
			
			src = getPath(src,url);
		}
		var jssrcs = jssrc.split("|");	// 可以include多个js,用|隔开
		for(var i=0;i<jssrcs.length;i++){
			// 使用juqery的同步ajax加载js.
			// 使用document.write 动态添加的js会在当前js的后面,可能会有js引用问题
			// 动态创建script脚本,是非阻塞下载,也会出现引用问题
			$.ajax({type:'GET',url:getPath(jssrc,src),async:false,dataType:'script'});
		}
	}

 

  在a.js中直接使用 include("../js/common.js");

 

多请求的问题

  使用上面的include看上去挺爽的,不过却带来另外1个严重的问题,就是多发送了1个ajax的请求。

  我们常常为了WEB性能,而合并js,减少请求。但使用include后却偏偏多了请求。如果这个问题不解决,相信很多人都不会在正式产品中使用include的了,除非是局域网产品。

 

  如何解决这个多请求的问题,我也思考很久,最后觉的单单使用客户端js是没办法解决了。所以就想到了使用服务端代码来解决

  还记的我之前有文章介绍 "js、css的合并、压缩、缓存管理"的时候,就通过服务器端代码在程序启动时候去合并js。

 

  所以我把include多请求的解决方案也加到里面去。就是在程序启动的时候去查找所有的js,发现有使用include的就把include中common.js的源代码替换该include函数。这样a.js中在运行的时候就没有include函数,而是真真包含了common.js的内容的js文件

 

后语

  丫的。说到最后,怎么又把所有的include都替换掉了,哪之前说的那么多不白说了。

 

  个人觉得,每个产品都应该要区分开发环境和产品环境(一般通过配置文件进行区分),在开发环境应该以开发效率为首要,而产品环境则以性能为首。所以这里的inlcude就应该要区分对待,在开发环境中使用js include来提高开发和维护效率,而在产品环境中则自动把所有include替换成真真的js文件的内容。

 

  都说完了,欢迎大家拍砖讨论。

[作者]:BearRui(AK-47)
[博客]: http://www.cnblogs.com/BearsTaR/
[声明]:本博所有文章版权归作者所有(除特殊说明以外),转载请注明出处.
绿色通道:好文要顶关注我收藏该文与我联系
posted on 2010-08-05 08:51 BearRui(AK-47) 阅读(5039) 评论(33) 编辑 收藏
发表评论
1888229
 回复 引用 查看   
#1楼 2010-08-05 09:12 | 风海迷沙      
汗,当然是使用母板页了。
 回复 引用 查看   
#2楼[楼主] 2010-08-05 09:14 | BearRui(AK-47)      
引用风海迷沙:汗,当然是使用母板页了。


但你不一定能保证所有用到a.js的页面都是使用同1个母板页啊。

 回复 引用 查看   
#3楼 2010-08-05 09:29 | Ahriman      
沙发.关注
 回复 引用 查看   
#4楼[楼主] 2010-08-05 09:34 | BearRui(AK-47)      
@Ahriman

你应该是地板了,哈哈

 回复 引用 查看   
#5楼 2010-08-05 09:37 | 风海迷沙      
@BearRui(AK-47)
这就能体现出jquery,ajaxtoolkit等的js库的好处了。。。

 回复 引用 查看   
#6楼 2010-08-05 09:39 | szyicol      
文章开头有个笔误

"但假设有5个页面用到了a.js" 应该是common.js吧



不过话说回来,如果加了一个common2.js 你还不是要去改5个页面的include!


应该直接固定一个common目录,并加一个配置文件,根据配置文件中的需要为每个页面来引用相应的common,这样就不用每个页面去改了,只改配置页面就行了,如果是每个页面都要加载的,连配置文件都可以省了!




 回复 引用 查看   
#7楼[楼主] 2010-08-05 09:42 | BearRui(AK-47)      
@风海迷沙

不用jquery,自己写也一样可以实现,不过麻烦点,现在大家都不自己写了,都用jq了。

 回复 引用 查看   
#8楼[楼主] 2010-08-05 09:44 | BearRui(AK-47)      
@szyicol
呵呵,没有笔误,是5个页面用到了a.js,因为a.js都需要common.js,所以如果不用include,5个页面都需要使用<script 加载common.js

如果加加1个common2.js,在需要把a.js中的include("../js/common.js")改成include("../js/common.js|../js/common2.js") 就行了,只改1个地方。

 回复 引用 查看   
#9楼 2010-08-05 09:46 | szyicol      
@BearRui(AK-47)

是我理解错了,

不过话又说回来,这个a.js不就跟母版页一样了吗?

 回复 引用 查看   
#10楼[楼主] 2010-08-05 09:52 | BearRui(AK-47)      
@szyicol
就是一楼说的哪样,不能保证每个用到a.js的页面使用都是相同的母版页啊。

而且有些语言(jsp,php)中都没有母版页。

 回复 引用 查看   
#11楼 2010-08-05 10:07 | Square      
不管是Java还是.net总是有继承吧?
你可以以一个基类实现page,然后在该基类中实现一个方法,此方法去获取一个XML配置中的相应所需的JS文件,这样就可以达到动态的申请和变更,

 回复 引用 查看   
#12楼[楼主] 2010-08-05 10:22 | BearRui(AK-47)      
@Square
呵呵,java不是1个page类对1个jsp,不过也可以通过其他方法实现你说的。但你说的就是完全使用服务器端代码实现,完全实现服务器端代码就有2个问题.
1、服务器端动态生成js,产品环境请求比较高的情况下,每次请求都需要生成,会不会很耗资源。
2、用服务器端代码实现,每次修改你说的xml配置,是否都需要重新启动应用。

 回复 引用 查看   
#13楼 2010-08-05 10:38 | mmyijia.com~      
收藏了,之前用的都是前两种方法
 回复 引用 查看   
#14楼[楼主] 2010-08-05 10:44 | BearRui(AK-47)      
@mmyijia.com~
谢谢支持,^_^

 回复 引用 查看   
#15楼 2010-08-05 11:19 | 阿瑞|www.16hi.com      
没弄明白想干啥 :)
 回复 引用 查看   
#16楼[楼主] 2010-08-05 11:29 | BearRui(AK-47)      
@阿瑞|www.16hi.com
看刚开始说的场景

 回复 引用 查看   
#17楼 2010-08-05 11:59 | Alex He      
it depends
 回复 引用 查看   
#18楼[楼主] 2010-08-05 12:15 | BearRui(AK-47)      
引用Alex He:it depends


???

 回复 引用 查看   
#19楼 2010-08-05 13:34 | dangjian      
嗯,是个好办法。
 回复 引用 查看   
#20楼 2010-08-05 13:40 | Otis's Technology Space      
不好调试嘿,要用log来调试
 回复 引用 查看   
#21楼[楼主] 2010-08-05 13:46 | BearRui(AK-47)      
@Otis's Technology Space

怎么不好调试,这个跟调试没关系吧,之前js怎么调试,现在就怎么调试啊。

 回复 引用 查看   
#22楼[楼主] 2010-08-05 13:46 | BearRui(AK-47)      
引用dangjian:嗯,是个好办法。


嘿嘿,谢谢

 回复 引用 查看   
#23楼 2010-08-05 13:50 | 蓝蓝的天      
主题不明确
 回复 引用 查看   
#24楼 2010-08-05 14:38 | icjyw.com      
不错啊
 回复 引用 查看   
#25楼[楼主] 2010-08-05 14:41 | BearRui(AK-47)      
哈哈,看来有人觉得好,有人觉得不好啊。

大家各取所需吧

 回复 引用 查看   
#26楼 2010-08-05 18:55 | icjyw.com      
写的还是很好的方法
 回复 引用 查看   
#27楼[楼主] 2010-08-05 19:33 | BearRui(AK-47)      
@icjyw.com
呵呵,谢谢支持

 回复 引用 查看   
#28楼 2010-08-05 21:06 | 不戒大师      
可以参考一:sg.dipan.com
这个游戏也是动态载入JS的

 回复 引用 查看   
#29楼[楼主] 2010-08-05 21:33 | BearRui(AK-47)      
引用不戒大师:
可以参考一:sg.dipan.com
这个游戏也是动态载入JS的


谢谢

 回复 引用 查看   
#30楼 2010-08-05 23:20 | 小生      
如果一个页面需要a.js和b.js,而a.js和b.js都需要common.js,
那么按照楼主的方法,common.js就会被请求两次,
贴出我的方案:
(function() {
var myJsC = {}; var myCssC = {}; var JsClass = {}; var CurDomain = location.protocol + "//" + document.domain.toLowerCase();
this.LoadJs = function(url, callback) {
if (!myJsC[url]) jQuery("<script type=\"text/javascript\" src=\"" + url + "\"><\/script>").appendTo(jQuery("head"));
myJsC[url] = true; if (callback) callback();
};
this.LoadCss = function(url, callback) {
if (!myCssC[url]) jQuery(" <link rel=\"stylesheet\" type=\"text/css\" href=\"" + url + "\" \/>").appendTo(jQuery("head"));
myCssC[url] = true; if (callback) callback();
}
jQuery("head script").each(function(i) { if (this.src) myJsC[this.src.replace(CurDomain, "")] = true; });
jQuery("head link").each(function(i) { if (this.href) myCssC[this.href.replace(CurDomain, "")] = true; });
})();

这种方案有个缺点,就是不方便调试,郁闷

 回复 引用 查看   
#31楼[楼主] 2010-08-06 00:24 | BearRui(AK-47)      
@小生
确实存在你说的请求2次的问题,不过因为这个函数只是在开发环境中使用,所以没做优化。

当然像你这样加1个对象记录已加载过的js也不错。

你的方法应该会出现我前面说的引用的问题。可以这样测试,在common.js中声明1个变量 var test = false;

a.js这样写
LoadJs("common.js");
alert(test);

看是否会报错。

 回复 引用 查看   
#32楼 2010-08-06 10:43 | Laoshu133      
不好意思小小的测试了下你的代码,开始还真没注意new RegExp("\\.\\./","g"); 这样需要两次转义,以前我只一直这么写的 /\.\.\//g 受教不少, 不过有个地方貌似有点问题lastIndex = absolutePath.lastIndexOf("/",lastIndex); 这样写貌似返回的不是正确结果

lastIndex = absolutePath.lastIndexOf("/",lastIndex-1);

 回复 引用 查看   
#33楼[楼主] 2010-08-06 10:52 | BearRui(AK-47)      
@Laoshu133
恩,谢谢指出问题。测试的时候可能没有测试返回多级目录,所以没发现,^_^

刷新评论列表  刷新页面  返回页首
发表评论

昵称: [登录] [注册]

主页:

邮箱:(仅博主可见)

验证码: 验证码 看不清,换一个

评论内容:

  登录  注册

[使用Ctrl+Enter键快速提交评论]

0 1792626 vVoxnRLNDgU=
首页博问闪存新闻园子招聘知识库
最新IT新闻:
· 报告称Android广告印象份额同比增长504%
· 百度高管称正调研东南亚市场 未来或建分公司
· 东芝索尼获得日本国有银行逾十亿美元贷款
· Getjar发布Android虚拟支付系统
· AMD遭苹果三星等挖角 英特尔或已非最大威胁
» 更多新闻...
最新知识库文章:
· 编程的艺术:漂亮的代码和漂亮的软件
· GIT分支管理是一门艺术
· 编程:是一门艺术
· 编程是一门艺术吗?
· 对Java初学者的忠告
» 更多知识库文章...

China-pub 2011秋季教材巡展
China-Pub 计算机绝版图书按需印刷服务

抓虾
google reader
鲜果
哪吒
QQ邮箱
http://wap.feedsky.com/BearRui
昵称:BearRui(AK-47)
园龄:5年10个月
荣誉:推荐博客
粉丝:127
关注:3

搜索

 

最新随笔

  • 1. 产生唯一随机码的方法分析。
  • 2. URL中允许携带sessionid带来的安全隐患。
  • 3. JS 实现完美include
  • 4. 基于模板的excel导出
  • 5. 实现if elseif else的jsp标签。
  • 6. 记一复杂页面的前端优化(2) - 其他优化
  • 7. 记一复杂页面的前端优化(1) - 不一样的延迟加载
  • 8. 名站技术分析 — tudou网首页下列菜单的弹出效果
  • 9. LESS 让css也支持变量,运算符,include,嵌套规则等等
  • 10. CSS技巧 — 不使用图片实现圆角、阴影、渐变等功能

随笔分类(38)

  • .NET(11)
  • CSS(3)
  • JAVA(2)
  • javascript(4)
  • Life(5)
  • WEB(4)
  • 高性能WEB开发(5)
  • 浏览器(1)
  • 名站技术分析(3)

随笔档案(39)

  • 2010年10月 (1)
  • 2010年8月 (3)
  • 2010年7月 (5)
  • 2010年6月 (4)
  • 2010年5月 (6)
  • 2006年6月 (4)
  • 2006年5月 (7)
  • 2006年4月 (9)

积分与排名

  • 积分 - 178246
  • 排名 - 484

最新评论

阅读排行榜

评论排行榜

推荐排行榜

Copyright ©2012 BearRui(AK-47)