jsモジュールロードの詳細

9758 ワード

Javaの中の様々なimportロードを見て、振り返ってみるとjavascriptはまだ自分で車輪を作っていて、いろいろなXXのモジュールロードフレームワークを書いていて、ECMASCRIPT 6はいつ普及するか分かりません.しかし、DTはDTに戻り、学ぶべきことはやはり学ばなければならない.
同期ロードモード(SMD)
同期は、名前の通り、AモジュールがBモジュールのいくつかの関数を参照して実行するなど、順次ロードされます.この場合、Bモジュールはすでにページメモリに存在している必要があります.A呼び出しは、次の操作を順調に完了します.例は、ブラウザメモリにすでに存在するため、Aモジュールがdocumentオブジェクトを直接呼び出すことです.
同期モジュールコードの実現も比較的に簡単で、モジュールの経路を解析して、実行は関数を落として、モジュールを呼び出します
モジュールの定義
var manger = (function(){ var F = F || {}; F.define = function(str, fn){ var parts = str.split('.'), old = parent = this,//old      ,parent           i = len = 0; for(len = parts.length; i < len; i++){ if(typeof parent[parts[i]] === 'undefined'){ parent[parts[i]] = {}; } old = parent; parent = parent[parts[i]]; } if(fn){ old[parts[--i]] = fn(); } return F; } })();

上のコードでは、defineとmoduleはそれぞれモジュールの定義と呼び出しであり、モジュールは閉パッケージFに定義され、getF()呼び出しによって呼び出されます.ここでoldとparentの2つのオブジェクトが使用されているのは、現在のモジュールの親モジュールと祖父モジュールのキャッシュを保存するためであり、モジュールブロックを順次追加した後、現在のモジュールにコールバック関数を実行するためです.
ここでのコールバック関数は、jsファイルに書かれるモジュールの構築についてです.例えば、次のコードには、構造関数と静的メソッドが含まれています.
manger.getF().define('dom', function(){ var dom = function(id){ return document.getElementById(id); } dom.html = function(html){ return this.innerHTML; } return dom; });

 
モジュールを呼び出す
F.module = function(mod,callback){ var args = [].slice.call(arguments), fn = args.pop(), parts = args[0] && args[0] instanceof Array?args[0]:args, modules = [], modIds = '', i = 0, ilen = parts.length, parent, j, jlen; while(i < ilen){ if(typeof parts[i] === 'string'){ parent = this;//this      
                modIds = parts[i].replace('/^F\./', '').split('.');//modIds=[]
                for(j = 0, jlen = modIds.length; j < jlen; j++){ parent = parent[modIds[j]] || false; } modules.push(parent);//       modules ,        
            }else{ modules.push(parts[i]); } i++; } fn.apply(null, modules); }

 
モジュールオブジェクトをコールバック関数に導入してモジュールの呼び出しを実行するには、モジュールパスを解析し、ロードするモジュールをmodules配列に入れて実現することが重要です.例は以下の通り
manger.getF().module(['dom'],function(dom){ dom('div1'); });

 
デュアル非同期ロードモード
前言
上記の同期ロードモードから見ると、モジュールはメモリにロードされ始め、scriptラベルを非同期でバインドしてモジュールをロードしていません.では、他の人が書き終わっていないjsモジュールファイルをどのように参照するかという問題が発生し、非同期ロードの問題が発生しました.現在、各モジュールのロードフレームワークはこのように行われています.
セット定義と呼び出しを一体化したモジュールローダ
F.module = function(url, modDeps, modCallback){
    var args = [].slice.call(arguments);
    var callback = args.pop();
    var deps = (args.length && (args[args.length - 1] instanceof Array)) ? args
            .pop() : [];
    var url = args.length ? args.pop() : null, 
            params = [], //       ,        ,          
            depsCount = 0, //                  
            i = 0;//       
    var len;
    if (len = deps.length) {
        while(i<len){//        
            (function(i){//                ,                 ,   setModule
                depsCount++;
                loadModule(deps[i],function(mod){
                    params[i] = mod;//              params【i】
                    depsCount--;
                    if(depsCount===0){//
                        setModule(url,params,callback);
                    }    
                });
            })(i);
            /*depsCount++;
            loadModule(deps[i],function(mod){
                params[i] = mod;//              params【i】
                depsCount--;
                if(depsCount===0){//                ,            
                    setModule(url,params,callback);
                }    
            });*/
            i++;
        }
    }else{//             ,        
        setModule(url,[],callback);//setModule('lib/event',[],fn)
    }
}

上のコードで重要なのは、iを保存するために閉パッケージを使用することです.閉パッケージを使用しない場合、iの値は、毎回の結果ではなく、モジュールの数に最後に依存するだけです.閉鎖問題については私のブログを見ることができます.
コードに使われている2つの重要な関数はloadModuleとsetModuleです.以下、この2つのコードについて説明します.
loadModule = function(name, callback){
    var module;

    if (moduleCache[name]) {//        
        _module = moduleCache[name];
        if (_module.status = 'loaded') {//      
            //            ,           ,                 params[i]
            setTimeout(callback(_module.exports), 0);
        } else {//     ,             ,            onload    ,      
            _module.onload.push(callback);
        }
    } else {//       ,         ,onload             ,          ,          
        moduleCache[name] = {
            name : name,
            status : 'loading',
            exports : null,
            onload : [ callback ]
        };
        loadScript(getUrl(name));// js        
    }
    for ( var i in moduleCache) {
        console.log(moduleCache[i]);
    }
};

loadModule関数がモジュールをロードする場合は3つに分けられます.コード注釈にも書いてあります.肝心なのは、jsモジュールでjsファイルの状態が「loaded」であればすぐにcallbackコールバック関数を実行することです.ここでsettimeoutを0ミリ秒に遅延させる役割は、必ずしもすぐに実行するのではなく、次のtrickまで待って実行します.フロントエンドでは、すぐに実行されると理解され、callback関数は、モジュールを作成するときに関数returnのオブジェクトを指すモジュールの外部インタフェースを彼に渡します.
//   js  ,    js     ,params        ,           
setModule = function(name, params, callback){
    var _module, fn;
    if (moduleCache[name]) {//  (      )           ,          js  
        _module = moduleCache[name];
        _module.status = 'loaded';
        _module.exports = callback ? callback.apply(_module, params) : null;//exports          
        while (fn = _module.onload.shift()) {
            console.log('      :'+fn);
            console.log('    :'+_module.exports);
            for(var i in _module.exports){
                console.log(i);
            }
            fn(_module.exports);
        }
    } else {//    ,        ,        
        console.log('    :'+callback);
        for(var i in params){
            console.log('  '+i+':'+params[i]);
            if(typeof params[i]==='object'){
                for(var j in params[i]){
                    console.log(j+':'+params[i][j]);
                }
            }
        }
        callback && callback.apply(null, params);
    }
},

 
F.moduleでは、依存モジュールがメモリにロードされている場合にのみsetModuleを使用してモジュールを修正します.この関数は、モジュール属性(moduleName,status,exports,onload)をロード完了後の状態に修正し、コールバック関数を実行する役割を果たします.ここで実行されるコールバック関数は、loadModuleが実行するコールバック関数とは異なり、loadModuleは依存モジュールを実行するためのドロップダウンであり、ここでは最終モジュールを実行するコールバックである.
 
これにより、完全なモジュールローダが実現され、上記の非同期ローダはRequireJSに類似している.依存に関する場合は事前に定義され、定義されるとロードされなければならず、近接宣言の原則に合致しないため、最適化されるべき場所であり、批判の指摘も望ましい.