现在所有模块都将匿名化,同步化。新版的加载器相同说是相当于原生require的加强版,同时require多个模块时,返回一个数组。
var array = require(["./aaa","./bbb","./ccc"])
//相当于
var array = [];
array[array.length] = require("./aaa")
array[array.length] = require("./bbb")
array[array.length] = require("./ccc")
亦可以在后面加函数
var array = require(["./aaa","./bbb","./ccc"],function(a, b, c){
//.......................
})
模块允许多种定义法
//纯node.js方法
var a = require("httpflow")
console.log(a)
//CommonJS的匿名函数法
define({
aa:1,
bb:2
})
//CommonJS的匿名函数法
define(function(){
exports.aaa = "aaa"
})
//CommonJS的匿名函数法
define(function(){
exports.aaa = "aaa"
return "xxxx" //注意这时exports对象将变xxxx返回值所取替,当返回为不为undefined时
})
//CommonJS的匿名函数法
define(["./aaa"],function(require, exports, module){
console.log(module)//指向当前模块
})
//ADM的匿名函数法
define(["./aaa","./bbb"],function(a,b){
console.log(module)//指向当前模块,module总是存在的
console.log(a)
})
我们再看一下新旧模块系统实现的对比:
$.mix({
//模块加载的定义函数
define: function( name, deps, factory ){//模块名,依赖列表,模块本身
//这是一个空接口
},
//模块加载的请求函数
require: function( deps, factory, errback ){
var _deps = {}, args = [], dn = 0, cn = 0;
factory = typeof factory == "function" ? factory : $.noop;
String(deps +"").replace( $.rword, function( str ){
if(str.indexOf("./") === 0){
str = str.replace(/^\.\//, "" );
}
dn++;
var match = str.match( rmodule );
var id = "@"+ match[1];//模块的ID
var filename = match[2];//模块的URL
if(!filename){
id = id.replace(/\.js$/,"")
filename = $.path.join( factory.parent || $.require.root, match[1] ); //path.join会自动处理../的情况
filename = /\.js$/.test(filename) ? filename : filename +".js";
}
var input = id;
try{//先把它当成原生模块进行加载
returns[ id ] = require( match[1] );//require自身是缓存请求的
mapper[ id ] = {
state : 2
}
process.nextTick( $._checkDeps );//每成功加载一个模块就进行依赖检测
}catch(e){
input = filename
}
if( !_deps[ input ] ){
args.push( input );
_deps[ input ] = "司徒正美";
}
if( input === filename && !mapper[ input ] ){ //防止重复生成节点与请求
mapper[ input ] = {};//state: undefined, 未安装; 1 正在安装; 2 : 已安装
loadJS( filename );
}else if( mapper[ input ].state === 2 ){
cn++;
}
});
var id = factory.id || "@cb"+ ( cbi++ ).toString(32);
if( typeof errback == "function" ){
errorStack.push( errback );//压入错误堆栈
}
mapper[ id ] = mapper[ id ] || {}
$.mix( mapper[ id ], {//创建或更新模块的状态
callback: factory,
id: id,
deps: _deps,
args: args,
state: 1
}, false);
//在正常情况下模块只能通过_checkDeps执行
loadings.unshift( id );
process.nextTick( $._checkDeps );
},
// 模块加载的检测依赖函数,如果一个模块所依赖的其他模块的状态都是2了,那么将它也改成2,并执行回调
_checkDeps: function (){
loop:
for ( var i = loadings.length, filename; filename = loadings[ --i ]; ) {
var obj = mapper[ filename ], deps = obj.deps || {};
for( var key in deps ){
if( deps.hasOwnProperty( key ) && mapper[ key ].state != 2 ){
continue loop;
}
}
//如果deps是空对象或者其依赖的模块的状态都是2
if( obj.state !== 2){
loadings.splice( i, 1 );//必须先移除再安装,防止在IE下DOM树建完后手动刷新页面,会多次执行它
obj.state = 2 ;
var id = obj.id;
var ret = collect_rets( id, obj.args ||[], obj.callback );
if( id.indexOf("@cb") === -1 ){
returns[ id ] = ret;
$.log("已加载" + id + "模块","cyan", 6 );
$._checkDeps();
}
}
}
}
});
//把模块有关信息都存放在这里
var mapper = $.require.cache = {}
//从returns对象取得依赖列表中的各模块的返回值
function collect_rets( name, args, fn ){
for(var i = 0, argv = []; i < args.length ; i++){
argv.push( returns[ args[i] ] );
}
var ret = fn.apply( null, argv );//执行模块工厂,然后把返回值放到returns对象中
$.debug( name );//想办法取得函法中的exports对象
return ret;
}
//模块加载的加载函数
function loadJS( filename ){
try{
$.define = function(){//诡变的$.define
var args = Array.apply([],arguments);
if( typeof args[1] === "function" ){//处理只有两个参数的情况
[].splice.call( args, 1, 0, "" );
}
args[2].id = filename; //模块名
args[2].parent = filename.slice(0, filename.lastIndexOf( $.path.sep ) + 1) //取得父模块的文件夹
mapper[ filename ].state = 1;
process.nextTick( $._checkDeps );//每成功加载一个模块就进行依赖检测
$.require( args[1], args[2] );
}
require( filename );
}catch( e ){
$.log( e, "red", 3);
for(var fn; fn = errorStack.shift(); ){
fn();//打印错误堆栈
}
}
}
//用于模块加载失败时的错误回调
var errorStack = [];
新加载系统则精简许多了
var rparams = /[^\(]*\(([^\)]*)\)[\d\D]*/
var toString = ({}).toString;
global. define = function(deps, callback){
var caller = arguments.callee.caller
var args = caller.arguments;//取得当前模块的参数列表,依次为exports, require, module, __filename,__dirname
var common = {
exports: args[0],
require: args[1],
module: args[2]
}
var array = [], ret;
if(arguments.length === 1 && toString.call(deps) === "[object Object]"){
ret = deps;//如果是对象,那么它就是exports
}else if(typeof deps == "string" ){
deps = deps.match( $.rword );//如果依赖列表是字符串,则转换为数组
}
if(Array.isArray(deps)){//如果存在依赖关系,先加载依赖关系
for(var i = 0,el ; el = deps[i++] ;){
array[ array.length ] = args[1]( el );//require某个模块
}
}
callback = arguments[arguments.length - 1];
if(typeof callback == "function"){
var match = callback.toString().replace(rparams,"$1") || [];
var a = common[match[0]];
var b = common[match[1]];
var c = common[match[2]];
if( a && b && c && a != b && b != c && a != c ){//exports, require, module的位置随便
ret = callback.apply(0, [a, b, c]);
}else{
ret = callback.apply(0, array);
}
}
if(typeof ret !== "undefined"){
args[2].exports = ret;
}
return args[2].exports;
}
var $ = {}
$.require = function(deps, callback){
if(typeof deps == "string"){
return require(deps)
}
var array = [];
for(var i = 0, el; el = deps[i++];){
array.push( require(el) )
}
if(typeof callback == "function"){
callback.apply(0, array);
}
return array
}
详细的测试用例请见app\public\scripts\test