RubyLouvre / newland

node.js 全栈式MVC framework
368 stars 90 forks source link

全新的加载器(兼容CommonJS与Require的AMD与SeaJS) #5

Open RubyLouvre opened 12 years ago

RubyLouvre commented 12 years ago

现在所有模块都将匿名化,同步化。新版的加载器相同说是相当于原生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

wbpmrck commented 11 years ago

请问一下正美大大,你这个东东是基于nodejs的一个Web框架对吗,里面包含你之前的模块加载等框架不。是不是你之前做的前端的好东东都融合在里面了?