JSモジュール化が簡単に実現できます.

7530 ワード

以前は時間をかけてRequireJSソースコードを見に行きました.二千行ぐらいのコードですが、私の愚かさを許してくれました.本当に少し分かりません.考えてみます.やはり自分で一つ実現してからにします.
単純な目標
requireJSのような機能を実現し、関連する機能
//     ,    
define(id, deps, factory)

//     ,    
require(deps, factory) 
簡単な分析
まず簡単な例をあげて、分析を行います.
require(['main'], function(main) {
    console.log('start~~~');
    main.hello();
});
  • プロセス解析
  • depsパラメータによって、ファイル依存スクリプトがmain.jsであることが分かりやすくなり、その後に依存スクリプトをロードし、その後に一回実行し、その結果をコールバック関数factoryのパラメータとして実行し、匿名のコールバック関数を実行すれば良い.
  • 問題分析
  • スクリプトをどうやって読み込むかというと、これは2つの方法を考えやすく、1つはajaxで要求し、もう1つはDom要素で追加してブラウザに読み込ませる.ajax要求はドメインをまたぐ問題があるので、Domロードの方式を採用します.
  • モジュールのロードが完了したとどう判断しますか?scriptのonloadイベントで対応するモジュールローディングを判断したいです.考え方を調整して、モジュールの状態を定義して、ロードする時、状態をロード中と表記します.レコードモジュールの依存数は、モジュールのすべての依存性が全部ロードされた場合、このモジュールの対応する出力としてコールバック関数が実行され、モジュールローディングが完了します.
  • は、上記の簡単な例のように、コールバック関数をモジュールonloadによってロード済みのコールバック関数としてどのように一時保存し、実行すればいいですか?依存するモジュールが複数ある場合は、ロードが完了するごとに、ロードが完了したかどうかを判断します.なお、この場合も、現在のモジュールのロードが完了したことを意味し、結果を返すことができます.
  • 循環依存問題はこんなに難しい問題です.後に残してください.
  • データ構造は、まずすべてのモジュールキャッシュオブジェクトを必要とし、ロードされたモジュールとローディングされているモジュールを記録する.モジュールのデータ構造を以下のように設計します.
    module[name] = {
        status: String,   //          'loading' or 'loaded'
        export: Object  , //           
        onload: Arrray[function]    //               
    }
    
  • 簡単に実現する
    女の子は才能がなくて、実現は主に葉のかんざしのブログの実現を参考にして、彼/彼女の実現を読んでから、自分で一回実現しました.
    //            
    (function(){
    
        //        
        var moduleMap = {};
        //      
        function define(id, deps, factory){ 
            var name;
    
            //         , factory   
            var params = [];
    
            if(typeof id == "object" ){ //   require(deps, factory)  
                factory = deps;
                deps = id;
            }
            else{                       //   deine(id,deps, factory) require(id)  
                name = id; 
            }
            //         
            var depCount = deps && deps.length;
            //       ,         
            if( !depCount ||  depCount == 0){
                return saveModule(name, null ,factory)
            }
            else{
                deps.forEach(function(dep, i){
                    //           
                    (function(i){
                        //        ,          
                        loadScript(dep, function(param){
                            //     ,     
                            depCount--;
                            params[i] = param;
                            //     0,        ,            
                            if(depCount==0){
                                return saveModule(name, params, factory);
                            }
                        })
                    }(i))
                })
            }
        }
    
        function getURL(name){
            if( name.indexOf('.js') < 0){
                return name + '.js';
            }
    
            return name;
        }
    
        function loadScript(name, callback){
            var mod = moduleMap[name];
    
            if( mod ){
                //       ,      
                if( mod.status == 'loaded' ){
                    callback & callback(mod.export);
                }
                else{ //     ,        
                    mod.onload.push(callback);
                }
            }
            else{
                //          ,      Dom  
                moduleMap[name] = {
                    status: 'loading',
                    export: null,
                    onload: [callback]
                }
    
                var url = getURL(name);
    
                var el = document.createElement('script');
                el.src = url;
                el.id = name;
                document.body.appendChild(el);
            }
    
        }
    
        //         
        function saveModule(name, params, callback){
            var mod = moduleMap[name];
            if( mod ){
                //         
                mod.status = 'loaded';  
    
                //       ,     export  
                mod.export = callback && callback.apply(this, params); 
    
                //             
                while( fn = mod.onload.pop() ){
                    fn && fn(mod.export);
                }
            }
            else{
                //       ,        ,  require(deps, factory)   
                callback.apply(this, params)
            }
        }
    
        //          
        window.define = define;
        window.require = define;
    
    })()
    
    テスト分析
    前の簡単な例では、ソースのDemoはここをクリックします.
    
    
    
    
        
         requireJS Demo -- my version 
    
    
    
     
     
    
    
    
    // index.js
    require(['main'], function(main) {
        console.log('start~~~');
        main.hello();
    });
    
    // main.js 
    define('main', [], function() { 
        console.log('require module: main'); 
        return {
            hello: function() {
                console.log('hello main');
            }
        };
    }); 
    
    // myRequire.js
    //      
    
    は を して し、 を ける.
  • mainは、requireが であり、nameが1である.depCountに することが かりました.main
  • を します.
  • は、loadScriptが されていないので、これを し、Dom を してmoduleMap['main']をロードする.この 、
    moduleMap['main'] = {
        status: 'loading',
        export: null,
        onload:[function(param){
            // depCount = 1
            depCount--;
            params[i] = param;
            //     0,        ,            
            if(depCount==0){
                return saveModule('', params, factory);
            }
        }] 
    } 
    
  • と されています.
  • main.jsは、 が したら、 の main.jsを します.
  • は、この に しないので、 define('main'...)
  • に ります.
  • は、saveModule('main'...)が されているので、 をmoduleMap['main']に えて、そのコールバック を し、loaded
    //     
    function() { 
        console.log('require module: main'); 
        return {
            hello: function() {
                console.log('hello main');
            }
        };
    }
    
  • に を えます.
  • コンソール export、このときの'require module: main'は、
    moduleMap['main'] = {
        status: 'loaded',
        export: {
            hello: function() {
                console.log('hello main');
            }
        },
        onload:[function(param){
            //     ,     
            depCount--;
            params[i] = param;
            //     0,        ,            
            if(depCount==0){
                return saveModule('', params, factory);
            }
        }] 
    } 
    
  • となる.
  • は、 のレベルのモジュールのコールバック moduleMap['main']を し、moduleMap['main'].onloadをパラメータ
  • とする.
  • この moduleMap['main'].exportparamsに わり、 [moduleMap['main'].export]は は1となりました. は0です.ちょうど のレベルのモジュールのdepCount
  • が されます.
  • は、saveModule('', params, factory)が されていないので、 にコールバック を します.すなわち、
    function(main) {
        console.log('start~~~');
        main.hello();
    }
    
    /*    main =  {
        hello: function() {
            console.log('hello main');
        }
    }*/
    
  • 10.コンソール moduleMap['']、および'start~~~'.
    これで が わります.
    を す
    ながら、この は を していません. もES 6とCommunJSのサイクル の を ましたが、ES 6は の を っていますので、 はありません.Common JSは に されている だけを します.
    いなことに、この は があると、プログラムは を たさないために を し、 も して することはない.
    :
  • リーフ・ガフ【モジュール プログラミング】リーク・ireJSを する- なモジュール・キャリア
  • を する.
  • JavaScriptモジュールの ローディング
  • はゼロからJavaScriptモジュール キャリア
  • を する.