文档章节

JS的AMD

阳光test
 阳光test
发布于 2013/03/14 22:08
字数 1936
阅读 783
收藏 11

            什么是AMD呢,引用维基百科的一段话吧:Asynchronous module definition (AMD) is a JavaScript API for defining modules such that the module and its dependencies can be asynchronously loaded. It is useful in improving the performance of websites by bypassing synchronous loading of modules along with the rest of the site content.

             更多信息可以直接查看维基百科:http://en.wikipedia.org/wiki/Asynchronous_module_definition

             我之前做JS开发,一般是这么做的:

              首先在页面的尾部写入script,script标签的src属性写入JS文件路径,然后再在这个JS文件中写入代码,如果有很多不同功能的代码,那么就多几个script。对一般的应用来说,这样就够了,但是如果你面临的是一个很复杂的工程,你现在需要写一个JS库来支持这些功能,你不可能把所有功能都写在一个JS文件中,所以就会按照一定的规则将这个库切分成多个文件,现在假设这个库有core,A,B,C,D这几块儿,core是基础,必须先载入,载入之后又要用到B,但是B又依赖于C,C又依赖于D,那么在写script标签的时候就必须特别注意引入这几块儿的顺序了,顺序错了,肯定JS就会报错。

              那么如果这样,每个页面我都需要特别注意script的顺序,并且一旦这个库修改了一下,比如B现在不光依赖C,还依赖E,那么只有在每个引入了B的html文件中修改,你想想,这样的工作量真心巨大啊。

              那么有什么办法可以解决这个问题呢?

              其实解决办法就是上面我提到的AMD。

              node.js有require,但是它是使用的同步方式,而我说的是采用异步方式。

              当然,现在市面上已经有很多这种加载器了,比如require.js,如果在实际项目中,可以直接使用这些成熟的加载器,但是为了学习,我觉得还是可以自己胡乱搞一搞。

              AMD规范中定义了define,定义如下:

                      define([module-name?], [array-of-dependencies?], [module-factory-or-object]);

             第一个参数是模块名,按照规范是可以省略的,第二个参数是这个模块所依赖的模块,第三个参数就是这个模块的实现。

             PS:我个人觉得第一个参数还是不要省略了,因为省略了感觉就不那么知名达意了。

             刚才的define函数是模块的定义,那么还需要有模块的加载,我个人JS比较弱,貌似没在AMD规范中找到模块加载的定义,因为我稍微看了一下kissy,kissy是使用的use,再想了一下,use这个名字的确不错,所以我也就使用use了。

             use这个函数有两个参数,第一个参数是要使用的模块名的列表,第二个参数是回调,也就是这些模块加载完成之后执行的函数,比如:use(["a","b"],function(a,b) {}),这里的回调函数需要将这两个模块作为参数传递进去。

             首先在讲define和use之前,需要写一下JS脚本加载的代码,这个代码实现的功能就是JS脚本的载入,当然,是异步的:

 

var _loadJs = function(file,callback) {
	callback = callback ? callback : function() {};
	var script = document.createElement("script");
	script.type = "text/javascript";
	script.async = true;
	if(script.readyState) {
		//IE
		script.onreadystatechange = function() {
			if(("loaded" === script.readyState) || ("complete" === script.readyState)) {
				script.onreadystatechange = null;
				callback();
			}
		}
	} else {
		script.onload = function() {
			callback();
		}
	}
	script.src = file;
	document.body.appendChild(script);
}


          首先说一下,我自己实现的这个加载器自己都感觉不是特别好,作为JS的初学者,希望各位大神不喜勿喷。

          首先是define,假设现在define('a',['b','c'],function(b,c) {}) ,因为a模块依赖b和c,那么就需要首先调用_loadJs这个函数将b和c载入,比如现在加载b,由于_loadJs有回调函数,所以我们可以在这个上面做一点文章,首先调用_loadJs之前,我将这个模块放入loading的队列中,加载完毕之后在回调函数中将b从loading队列中删除,并将它插入到loaded这个队列,define设定一个定时程序,定时去loaded队列中查询是否所有模块都已经在了,如果是,那么执行回调,否则,查看各个模块是否在loading队列,如果在,不操作,否则,调用_loadJs。

         use的时候,也需要查看loaded这个队列和loading队列,如果所有模块加载完成,那么执行回调。

         当然,在这儿,由于b和c需要作为参数传递进去,所以还需要有一个字面量的对象存储这些模块,这个我就不多说了。

          反正都丢人了,顺便也把自己这段垃圾代码贴出来吧!

     

(function() {
	/*
	思路:
	 tp.use中监测是否有依赖,若没有依赖,直接执行,如果有依赖,但依赖已经被载入,也直接执行,否则,将所依赖的模块进行for,并且查看是否这个模块正在被载入,如果没有正在被载入,那么进行load,
	 并且进行setInterval,如果检测到在loaded数组中找到所有的模块,进行clearInterval,并且执行
	 define中判定是否有依赖,如果没有,直接执行,如果有胆识依赖已经被载入,也直接执行,否则,同上进行for,setInterval发现所有已经loaded,clearInterval,执行回调,并且将这个模块放入loaded数组
	 */
	var _loaded = ['tp.core'], //已经被载入的模块,默认载入tp.core.js
		_loading = []; //正在被载入的模块
	/**
	 * AMD规范
	 * @param {String} moduleName 模块名,本来按照AMD规范是可以省略的,但此处不可省略
	 * @param {String|Array} deps 依赖的模块
	 * @param {Function} factory 回调
	 */
	tp.define = function(moduleName,deps,factory) {
		deps = _getDeps(deps);
		_checkWaitingStatus(deps,function() {
			_loaded.push(moduleName);
			_loading.remove(moduleName);
			factory.apply(factory,tp.modules.get(deps));
		});
	};
	window.define = tp.define; //将这个函数勾到window上面
	/**
	 * 使用模块
	 * @param {Array|String} deps 依赖的模块名
	 * @param {Function} callback 回调函数
	 */
	tp.use = function(deps,callback) {
		deps = _getDeps(deps);
		_checkWaitingStatus(deps,function() {
			callback.apply(callback,tp.modules.get(deps));
		});
	};
	function _getDeps(deps) {
		if(deps) {
			if(tp.type.isString(deps)) {
				//只有一个元素
				return [deps];
			} else {
				return deps;
			}
		}
		return [];
	}
	function _checkWaitingStatus(waiting,callback) {
		var waitingCount = waiting.length, //waiting的个数
			notLoadedCount = 0, //还没有被载入的模块个数
			suffix = tp.global.config.get("debug") ? ".js" : ".min.js",
			libBasePath = tp.global.config.get("libBasePath");
		for(var i = 0; i < waitingCount; i++) {
			if(!_loaded.isFind(waiting[i])) {
				//还没有被载入
				notLoadedCount ++;
				if(!_loading.isFind(waiting[i])) {
					_loading.push(waiting[i]);
					tp.load.asynJs(libBasePath + waiting[i] + suffix + "?time=" + tp.lastCompileTime,function() {});
				}
			}
		}
		if(0 == notLoadedCount) {
			callback();
		} else {
			var interval = setInterval(function() {
				if(_loaded.isFindAll(waiting)) {
					clearInterval(interval);
					callback();
				}
			},10);
		}
	}
})();


   这个地方的tp是一个字面量,定义如:var tp = tp || {},这个地方我把define挂在了window下面,但是use放在了tp这个命名空间下面,使用的时候是tp.use(),我写的这个库里面也有一些配置项,比如debug,如果debug为true,那么文件后缀就为.js,debug为false,那么文件后缀就为.min.js,因为我会对它进行压缩,由于浏览器会缓存JS,所以为了库有修改之后JS会更新,在JS文件名后面加了一个time,它的值是lstCompileTime,这个时间只要代码有修改就会改变。

     当时写这个的时候,网上有位童鞋说这种异步载入JS文件有一个好处:

        同步载入的时候即使缓存了JS文件,也就是304,浏览器还是会发出http请求,但是异步载入缓存之后不会发出http请求。

        我用fiddler抓包发现异步载入也还是有http请求,我也不知道孰对孰错,希望有知道的童鞋,不吝赐教。

        其实关于这个的思路,我参考了http://stylechen.com/easyjs-parallel-moduleloader.html,有兴趣的童鞋可以看看。

        好吧,差不多了。


              

© 著作权归作者所有

阳光test

阳光test

粉丝 542
博文 71
码字总数 91741
作品 1
杭州
程序员
私信 提问
JavaScript模块化进阶

写在前面 模块化简单来说就是是指把一个复杂的系统分解到多个模块以方便编码。JS模块化的大致流程为:CommonJS(服务端) -> AMD(浏览器端)-> UMD(兼容了CommonJS和AMD) -> ES Module(E...

Jee
2018/12/12
0
0
RequireJS与SeaJS模块化加载示例

web应用越变的庞大,模块化越显得重要,尤其Nodejs的流行,Javascript不限用于浏览器,还用于后台或其他场景时,没有Class,没有Package的Javascript语言变得难以管理,于是出现CommonJS项目...

nosand
2014/05/04
0
10
瞅瞅JavaScript模块标准

模块是每门语言构建复杂系统的必备特性,JavaScript自然也不例外。JavaScript当前流行的模块化标准有CommonJS、AMD、CMD、ES6等等,本文对这些标准做了简单梳理,努力做到应用时不懵逼,不乱...

疯魔程序员
2017/08/23
0
0
使用 RequireJS 优化 Web 应用前端

基于 AMD(Asynchronous Module Definition)的 JavaScript 设计已经在目前较为流行的前端框架中大行其道,jQuery、Dojo、MooTools、EmbedJS 等纷纷在其最新版本中加入了对 AMD 的支持。本文...

IBMdW
2012/09/11
8.6K
16
JS每日一题: 说说你对前端模块化的理解

20190114问: 说说你对前端模块化的理解 模块的定义: 可以理解成实现特定功能的相互独立的一组方法 为什么要使用模块化: 可维护性 命名空间 可复用性 模块化规范 CommonJS AMD UMD CMD Modul...

JS每日一题
01/23
0
0

没有更多内容

加载失败,请刷新页面

加载更多

掌握生成对抗网络(GANs),召唤专属二次元老婆(老公)不是梦

全文共6706字,预计学习时长12分钟或更长 近日,《狮子王》热映,其逼真的外形,几乎可以以假乱真,让观众不禁大呼:awsl,这也太真实了吧! 实体模型、CGI动画、实景拍摄、VR等技术娴熟运用...

读芯术
25分钟前
1
0
C#经典面试题100道

1. .NET和C#有什么区别 答:.NET一般指 .NET FrameWork框架,它是一种平台,一种技术。 C#是一种编程语言,可以基于.NET平台的应用。 2.一列数的规则如下: 1、1、2、3、5、8、13、21、34......

元歌
28分钟前
0
0
重磅!容器集群监控利器 阿里云Prometheus 正式免费公测

Prometheus 作为容器生态下集群监控的首选方案,是一套开源的系统监控报警框架。它启发于 Google 的 borgmon 监控系统,并于 2015 年正式发布。2016 年,Prometheus 正式加入 Cloud Native C...

阿里云云栖社区
30分钟前
1
0
LeetCode 160: 相交链表 Intersection of Two Linked Lists

爱写Bug(ID:iCodeBugs) 编写一个程序,找到两个单链表相交的起始节点。 Write a program to find the node at which the intersection of two singly linked lists begins. 如下面的两个链...

iCodeBugs
32分钟前
1
0
hadoop yarn漏洞 8088端口进入挖矿病毒处理记录

早上发现服务器cpu使用异常 进程如图所示 按照挖矿病毒的套路 肯定是定时任务不停地执行脚本 遂查看定时任务 进入/var/spool/cron 查看定时任务 发现里面有一个root文件 定时任务每分钟执行一...

詹姆斯-高斯林
35分钟前
0
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部