seed.js源碼分析

seed.js是基于AMD規(guī)范的,我的理解就是一幫子前端看到node.js的模塊化的好處之后,也想要享受這種好處,但是node因?yàn)槭呛蠖苏Z言,所有的資源運(yùn)行在本地機(jī)上面,可以通過系統(tǒng)調(diào)用直接調(diào)用,但是在瀏覽器端的js卻不能直接這么干,先不說沒有require,import,use,export之類的函數(shù),還有就是在瀏覽器沙箱里,請(qǐng)求的文件都是遠(yuǎn)端服務(wù)器上面的,要考慮各個(gè)模塊之間依賴有沒有加載完成,如果沒有加載完成直接運(yùn)行就會(huì)失效.
當(dāng)然,實(shí)現(xiàn)了把一大陀打散模塊化編程,在真正生產(chǎn)環(huán)境上,還要考慮效率的問題,資源可以合并到一個(gè)文件上面,這時(shí)候就需要用到打包工具了,常見的是grunt,gulp之類的,當(dāng)然,其實(shí)他們就是一個(gè)更加復(fù)雜的代碼解析工具,根據(jù)你的配置項(xiàng)進(jìn)行分析,正則匹配,把一些約定的地方進(jìn)行替換,然后可以進(jìn)行壓縮什么的

/*
* seed v1.1.4
* AMD module loader
*
* Copyright (c) 2013-2014 Yiguo Chan
* Released under the MIT Licenses
*
* Gighub : https://github.com/chenmnkken/seed.git
* Mail : chenmnkken@gmail.com
* Date : 2014-09-21
*/
(function( window, undefined ){

'use strict';

var seed = function(){
    var rProtocol = /^(file\:.+\:\/|[\w\-]+\:\/\/)/,
        rModId = /([^\/?]+?)(\.(?:js|css))?(\?.*)?$/,
        rReadyState = /loaded|complete|undefined/, 
       
        moduleCache = {},    // 模塊加載時(shí)的隊(duì)列數(shù)據(jù)存儲(chǔ)對(duì)象
        modifyCache = {},    // modify的臨時(shí)數(shù)據(jù)存儲(chǔ)對(duì)象
       
        head = document.head ||
        document.getElementsByTagName( 'head' )[0] ||
        document.documentElement,
       
        modClassName = 'seed_mod_' + ( +new Date() ) + ( Math.random() + '' ).slice( -8 ),
        isScriptOnload = !!document.addEventListener,
       
        // 模塊加載器的配置對(duì)象
        moduleOptions = {
            baseUrl : null,
            charset : {},    // 模塊對(duì)應(yīng)的charset存儲(chǔ)對(duì)象
            alias : {}
        };
       
    var easyModule = {
       
        error : function( msg ){
            throw new Error( msg );
        },
       
        // 用于合并模塊加載器配置的工具函數(shù)
        merge : function( key, options ){   
            if( options ){
                var name;
               
                for( name in options ){
                    moduleOptions[ key ][ name ] = options[ name ];
                }
            }
        },
       
        // 初始化模塊加載器時(shí)獲取baseUrl(既是當(dāng)前js文件加載的url)
        init : function(){
            var i = 0,
                script, scripts, initMod, initBaseUrl, url;
           
            // firefox支持currentScript屬性
            if( document.currentScript ){
                script = document.currentScript;
            }
            else{
                // 正常情況下,在頁面加載時(shí),當(dāng)前js文件的script標(biāo)簽始終是最后一個(gè)
                scripts = document.getElementsByTagName( 'script' );           
                script = scripts[ scripts.length - 1 ];
            }          
           
            initMod = script.getAttribute( 'data-main' );//主入口函數(shù),這樣只需加在tpl里面寫一行就夠了,自動(dòng)會(huì)加載的,或者類似sea.js一樣,直接下面再加幾行seajs.use()這樣
            initBaseUrl = script.getAttribute( 'data-baseurl' );           
            url = script.hasAttribute ? script.src : script.getAttribute( 'src', 4 );     
           
            // 如果seed是通過script標(biāo)簽inline添加到頁面中其baseUrl就是當(dāng)前頁面的路徑
            url = url || window.location.href;

            moduleOptions.baseUrl = initBaseUrl ?
                easyModule.mergePath( initBaseUrl, window.location.href ) :
                url.slice( 0, url.lastIndexOf('/') + 1 );
           
            // 初始化時(shí)加載data-main中的模塊
            if( initMod ){
                initMod = initMod.split( ',' );
                seedExports.use( initMod );
            }

            scripts = script = null;
        },
       
        // 獲取當(dāng)前運(yùn)行腳本的文件的名稱
        // 用于獲取匿名模塊的模塊名
        getCurrentScript : function(){
            var script, scripts, i, stack;
               
            // 標(biāo)準(zhǔn)瀏覽器(IE10、Chrome、Opera、Safari、Firefox)通過強(qiáng)制捕獲錯(cuò)誤(e.stack)來確定為當(dāng)前運(yùn)行的腳本
            // http://www.cnblogs.com/rubylouvre/archive/2013/01/23/2872618.html       
            try{
                // 運(yùn)行一個(gè)不存在的方法強(qiáng)制制造錯(cuò)誤
                easyModule.makeerror();
            }
            // 捕獲錯(cuò)誤
            // safari的錯(cuò)誤對(duì)象只有l(wèi)ine,sourceId,sourceURL
            catch( e ){
                stack = e.stack;
            }
           
            if( stack ){       
                // 取得最后一行,最后一個(gè)空格或@之后的部分
                stack = stack.split( /[@ ]/g ).pop();
                // 去掉換行符
                stack = stack[0] === '(' ? stack.slice( 1, -1 ) : stack.replace( /\s/, '' );
                //去掉行號(hào)與或許存在的出錯(cuò)字符起始位置
                return stack.replace( /(:\d+)?:\d+$/i, '' ).match( rModId )[1];            
            }
           
            // IE6-8通過遍歷script標(biāo)簽,判斷其readyState為interactive來確定為當(dāng)前運(yùn)行的腳本
            scripts = head.getElementsByTagName( 'script' );
            i = scripts.length - 1;
           
            for( ; i >= 0; i-- ){
                script = scripts[i];
                if( script.className === modClassName && script.readyState === 'interactive' ){
                    break;
                }
            }       
           
            return script.src.match( rModId )[1];
        },   
       
        // 將模塊標(biāo)識(shí)(相對(duì)路徑)和基礎(chǔ)路徑合并成新的真正的模塊路徑(不含模塊的文件名)
        mergePath : function( id, url ){
            var isRootDir = id.charAt(0) === '/',
                isHttp = url.slice( 0, 4 ) === 'http',
                domain = '',
                i = 0,
                protocol, isHttp, urlDir, idDir, dirPath, len, dir;

            protocol = url.match( rProtocol )[1];
            url = url.slice( protocol.length );
           
            // HTTP協(xié)議的路徑含有域名
            if( isHttp ){
                domain = url.slice( 0, url.indexOf('/') + 1 );
                url = isRootDir ? '' : url.slice( domain.length );
            }
           
            // 組裝基礎(chǔ)路徑的目錄數(shù)組
            urlDir = url.split( '/' );
            urlDir.pop();
           
            // 組裝模塊標(biāo)識(shí)的目錄數(shù)組
            idDir = id.split( '/' );
            idDir.pop();               

            if( isRootDir ){
                idDir.shift();
            }     

            len = idDir.length;           
           
            for( ; i < len; i++ ){
                dir = idDir[i];
                // 模塊標(biāo)識(shí)的目錄數(shù)組中含有../則基礎(chǔ)路徑的目錄數(shù)組刪除最后一個(gè)目錄
                // 否則直接將模塊標(biāo)識(shí)的目錄數(shù)組的元素添加到基礎(chǔ)路徑的目錄數(shù)組中       
                if( dir === '..' ){
                    urlDir.pop();
                }
                else if( dir !== '.' ){
                    urlDir.push( dir );
                }
            }

            // 基礎(chǔ)路徑的目錄數(shù)組轉(zhuǎn)換成目錄字符串
            dirPath = urlDir.join( '/' );   
            // 無目錄的情況不用加斜杠
            dirPath = dirPath === '' ? '' : dirPath + '/';       
            return protocol + domain + dirPath;
        },
       
        /*
         * 解析模塊標(biāo)識(shí),返回模塊名和模塊路徑
         * @parmm { String } 模塊標(biāo)識(shí)
         * @param { String } 基礎(chǔ)路徑baseUrl
         * @return { Array } [ 模塊名, 模塊路徑 ]
         * =====================================================================
         * 解析規(guī)則:
         * baseUrl = http://easyjs.org/js/                               
         * http://example.com/test.js => [ test, http://example.com/test.js ]
         *                  style.css => [ test, http://easyjs.org/js/style.css ]
         *                   ajax/xhr => [ xhr, http://easyjs.org/js/ajax/xhr.js ]
         *                    ../core => [ core, http://easyjs.org/core.js ]
         *                    test.js => [ test, http://easyjs.org/js/test.js ]
         *                       test => [ test, http://easyjs.org/js/test.js ]
         *          test.js?v20121202 => [ test, http://easyjs.org/js/test.js?v20121202 ]
         * =====================================================================
         */
        parseModId : function( id, url ){
            var isAbsoluteId = rProtocol.test( id ),       
                result = id.match( rModId ),
                modName = result[1],
                suffix = result[2] || '.js',
                search = result[3] || '',
                baseUrl, modUrl; 
           
            // 模塊標(biāo)識(shí)為絕對(duì)路徑時(shí),標(biāo)識(shí)就是基礎(chǔ)路徑
            if( isAbsoluteId ){               
                url = id;
                id = '';
            }

            baseUrl = easyModule.mergePath( id, url );
            modUrl = baseUrl + modName + suffix + search;
            return [ modName, modUrl ];
        },
       
        /*
         * 將依賴模塊列表的外部接口(exports)合并成arguments
         * @param { Array }    依賴模塊列表
         * @return { Array } 返回參數(shù)數(shù)組
         */
        getExports : function( deps ){
            if( deps ){
                var len = deps.length,
                    module = seedExports.module,
                    arr = [],
                    i = 0,
                    j = 0,
                    dep;
                   
                for( ; i < len; i++ ){
                    arr[ j++ ] = module[ deps[i] ].exports;
                }
               
                return arr;
            }
           
            return [];
        },   
       
        /*
         * 測(cè)試該模塊的依賴模塊是否都已加載并執(zhí)行完
         * @param { Object } 模塊對(duì)象
         * @return { Boolean } 依賴模塊是否都加載并執(zhí)行完
         */   
        isLoaded : function( mod ){
            var deps = mod.deps,
                len = deps.length,
                module = seedExports.module,
                i = 0, depMod;

            for( ; i < len; i++ ){
                depMod = module[ deps[i] ];
                if( depMod.status !== 4 ){
                    return false;
                }
            }
           
            return true;
        },
       
        factoryHandle : function( name, mod, factory, data ){
            // 模塊解析完畢,所有的依賴模塊也都加載完,但還未輸出exports
            mod.status = 3;       

            var args = easyModule.getExports( mod.deps ),  
                exports = typeof factory === 'function' ? factory.apply( null, args ) : factory;
           
            if( exports !== undefined ){
                // 如果有綁定modify方法,將在正式返回exports前進(jìn)行修改
                if( modifyCache[name] ){
                    exports = modifyCache[ name ]( exports );
                    // 修改后即刪除modify方法
                    delete modifyCache[ name ];
                }
                // 存儲(chǔ)exports到當(dāng)前模塊的緩存中
                mod.exports = exports;
            }
           
            // 當(dāng)前模塊加載并執(zhí)行完畢,exports已可用
            mod.status = 4;
           
            if( data ){
                data.length--;
            }       
        },
       
        /*
         * 觸發(fā)被依賴模塊的factory
         * @param { Object } 模塊的緩存對(duì)象
         */   
        fireFactory : function( useKey ){
            var data = moduleCache[ useKey ],
                factorys = data.factorys,
                result = factorys[0],           
                args, exports, name, toDepMod;  
               
            if( !result ){
                return;
            }
           
            name = result.name;
            toDepMod = seedExports.module[ name ];

            if( easyModule.isLoaded(toDepMod) ){
                factorys.shift();
                easyModule.factoryHandle( name, toDepMod, result.factory, data );           
               
                if( factorys.length ){
                    easyModule.fireFactory( useKey );
                }        
            }    
        },
       
        /*
         * 模塊加載完觸發(fā)的回調(diào)函數(shù)
         * @param{ Object } 模塊對(duì)象
         */
        complete : function( mod ){
            var module = seedExports.module,
                useKey = mod.useKey,
                keyLen = useKey.length,
                k = 0,
                namesCache, args, cacheLen, cacheMod, name, data, key, i, j;

            for( ; k < useKey.length; k++ ){
                key = useKey[k];
                data = moduleCache[ key ];
                useKey.splice( k--, 1 );

                if( !data ){
                    continue;
                }

                // 隊(duì)列沒加載完將繼續(xù)加載
                if( data.urls.length ){
                    easyModule.load( key );
                }
                else if( !data.length ){
                    namesCache = data.namesCache;                       
                    cacheLen = namesCache.length;
                    args = [];
                    i = 0;
                    j = 0;
                   
                    // 合并模塊的exports為arguments
                    for( ; i < cacheLen; i++ ){ 
                        name = namesCache[i];
                        cacheMod = module[ name ];
                       
                        if( cacheMod.status !== 4 ){
                            return;
                        }
                        args[ j++ ] = cacheMod.exports;
                    }
                   
                    // 執(zhí)行use的回調(diào)
                    if( data.callback ){
                        data.callback.apply( null, args );
                    }

                    // 刪除隊(duì)列數(shù)據(jù)
                    delete moduleCache[ key ];           
                }       
            }
        },
       
        /*
         * 創(chuàng)建script/link元素來動(dòng)態(tài)加載JS/CSS資源
         * @param{ String } 模塊的URL
         * @param{ String } 模塊名
         * @param{ String } 用來訪問存儲(chǔ)在moduleCache中的數(shù)據(jù)的屬性名
         * @return { HTMLElement } 用于添加到head中來進(jìn)行模塊加載的元素
         */
        create : function( url, name, useKey ){
            var charset = moduleOptions.charset[ name ],        
                mod = seedExports.module[ name ],
                script, link;
               
            mod.status = 1;//1,2,3,4用于存狀態(tài)標(biāo)識(shí)符
           
            // CSS模塊的處理
            if( ~url.indexOf('.css') ){
                link = document.createElement( 'link' );           
                link.rel = 'stylesheet';
                link.href = url;

                if( charset ){
                    link.charset = charset;
                }
               
                link.onload = link.onerror = function(){
                    link = link.onload = link.onerror = null;
                    mod.status = 4;               
                    moduleCache[ useKey ].length--;
                    easyModule.fireFactory( useKey );
                    easyModule.complete( mod );
                };           
               
                return link;
            }
           
            // JS模塊的處理
            script = document.createElement( 'script' );
            script.className = modClassName;
            script.async = true;       

            if( charset ){
                script.charset = charset;
            }
           
            if( isScriptOnload ){
                script.onerror = function(){
                    script.onerror = script.onload = null;
                    head.removeChild( script );
                    script = null;
                   
                    easyModule.error( '[' + name + '] module failed to load, the url is ' + url + '.' );
                };
            }

            script[ isScriptOnload ? 'onload' : 'onreadystatechange' ] = function(){
                if( isScriptOnload || rReadyState.test(script.readyState) ){
                    script[ isScriptOnload ? 'onload' : 'onreadystatechange' ] = null;
                    head.removeChild( script );
                    script = null;
                    // 加載成功
                    easyModule.complete( mod );
                }
            };
           
            script.src = url; 
            return script;
        },
       
        /*
         * 加載模塊
         * @param { String } 用來訪問存儲(chǔ)在moduleCache中的數(shù)據(jù)的屬性名
         */
        load : function( useKey ){           
            var data = moduleCache[ useKey ],
                module = seedExports.module,
                names = data.names.shift(),
                urls = data.urls.shift(),           
                len = urls.length,
                i = 0,
                mod, script;    

            for( ; i < len; i++ ){
                mod = module[ names[i] ];

                if( mod.useKey === undefined ){
                    mod.useKey = [];
                }

                mod.useKey.push( useKey );

                if( module[names[i]].status === undefined ){
                    script = easyModule.create( urls[i], names[i], useKey );
                    head.insertBefore( script, head.firstChild );
                }
                else{
                    data.length--;
                }
            }
        }  
    };       
   
    var seedExports = {
   
        version : '1.1.2',
       
        module : {},
   
        use : function( ids, fn ){
            ids = typeof ids === 'string' ? [ ids ] : ids;
            var alias = moduleOptions.alias,
                module = seedExports.module,
                len = ids.length,
                isLoaded = false,
                namesCache = [],
                modNames = [],
                modUrls = [],
                j = 0,
                mod, modName, result, useKey, args, name, i, id;    
               
            for( i = 0; i < len; i++ ){
                id = ids[i];
               
                // 獲取解析后的模塊名和url
                result = easyModule.parseModId( alias[id] || id, moduleOptions.baseUrl );
                modName = alias[ id ] ? id : result[0];
                mod = module[ modName ];   

                if( !mod ){
                    mod = module[ modName ] = {};
                    isLoaded = false;
                }
                else if( mod.status === 4 ){
                    isLoaded = true;
                }

                // 將模塊名和模塊路徑添加到隊(duì)列中
                modNames[ modNames.length++ ] = modName;
                modUrls[ modUrls.length++ ] = mod.url = result[1];
            }
           
            // 生成隊(duì)列的隨機(jī)屬性名
            useKey = modNames.join( '_' ) + '_' + ( +new Date() ) + ( Math.random() + '' ).slice( -8 );
            // 復(fù)制模塊名,在輸出exports時(shí)會(huì)用到
            namesCache = namesCache.concat( modNames );
           
            // 在模塊都合并的情況下直接執(zhí)行callback
            if( isLoaded ){
                len = namesCache.length;
                args = [];
               
                // 合并模塊的exports為arguments
                for( i = 0; i < len; i++ ){
                    name = namesCache[i];
                    mod = module[ name ];
                   
                    if( mod.status !== 4 ){
                        easyModule.error( '[' + name + '] module failed to use.' );
                    }
                   
                    args[ j++ ] = mod.exports;
                }
               
                // 執(zhí)行use的回調(diào)
                if( fn ){
                    fn.apply( null, args );
                }
               
                return;
            }
           
            // 添加隊(duì)列
            moduleCache[ useKey ] = {
                length : namesCache.length,
                namesCache : namesCache,
                names : [ modNames ],
                urls : [ modUrls ],
                callback : fn,
                factorys : [],           
                deps : {}
            };

            // 開始加載
            easyModule.load( useKey );
        },
       
        /*
         * 給模塊添加modify方法以便在正式返回exports前進(jìn)行修改
         * @param { String } 模塊名
         * @param { Function } 修改exports的函數(shù),該函數(shù)至少要有一個(gè)返回值
         */
        modify : function( name, fn ){       
            modifyCache[ name ] = fn;
        },
       
        /*
         * 修改模塊加載器的配置
         * @param { Object }
         */
        config : function( options ){
            var baseUrl, isHttp;
       
            if( options.baseUrl ){
                baseUrl = options.baseUrl;
                isHttp = baseUrl.slice( 0, 4 ) === 'http';
               
                if( isHttp ){
                    moduleOptions.baseUrl = baseUrl;
                }
                // 相對(duì)路徑的baseUlr是基于HTML頁面所在的路徑(無論是http地址還是file地址)
                else{
                    moduleOptions.baseUrl = easyModule.mergePath( baseUrl, window.location.href );
                }
            }
           
            easyModule.merge( 'charset', options.charset );
            easyModule.merge( 'alias', options.alias );
        }
   
    };  
   
     /*
      * 定義模塊的全局方法(AMD規(guī)范)
      * @param { String } 模塊名
      * @param { Array } 依賴模塊列表,單個(gè)可以用字符串形式傳參,多個(gè)用數(shù)組形式傳參
      * @param { Function } 模塊的內(nèi)容
      * factory的參數(shù)對(duì)應(yīng)依賴模塊的外部接口(exports)
      */
    window.define = function( name, deps, factory ){
        //類似于jquery的超級(jí)方法$
        if( typeof name !== 'string' ){
            if( typeof name === 'function' ){
                factory = name;
            } 
            else{
                factory = deps;
                deps = name;
            }
            name = easyModule.getCurrentScript();
        }
        else if( deps !== undefined && factory === undefined ){
            factory = deps;
            deps = null;
        }

        var alias = moduleOptions.alias,//moduleOptions是一個(gè)配置項(xiàng)的字面量
            module = seedExports.module,
            mod = module[ name ],
            isRepeat = false,
            isLoaded = true,
            names = [],
            urls = [],
            insertIndex = 0,
            pullIndex = 0,
            useKey, data, modUrl, factorys, baseUrl, depMod, depName, result, exports, args, depsData, repeatDepsData, i, repeatName, dep;
        // 在模塊都合并的情況下直接執(zhí)行factory
        if( !mod ){
            mod = module[ name ] = {};
           
            if( deps ){
                mod.deps = deps;
            }
           
            easyModule.factoryHandle( name, mod, factory );
            return;
        }
       
        useKey = mod.useKey[0];
        data = moduleCache[ useKey ];
        modUrl = mod.url;

        // 開始解析模塊內(nèi)容
        mod.status = 2;
        mod.deps = [];

        // 如果有依賴模塊,先加載依賴模塊
        if( deps && deps.length ){    
            // 依賴模塊的baseUrl是當(dāng)前模塊的baseUrl
            baseUrl = modUrl.slice( 0, modUrl.lastIndexOf('/') + 1 );       
            factorys = data.factorys;
            depsData = data.deps[ name ] = {};

            // 遍歷依賴模塊列表,如果該依賴模塊沒加載過,
            // 則將該依賴模塊名和模塊路徑添加到當(dāng)前模塊加載隊(duì)列的數(shù)據(jù)去進(jìn)行加載
            for( i = 0; i < deps.length; i++ ){             
                dep = deps[i];            
                result = easyModule.parseModId( alias[dep] || dep, baseUrl );
                depName = alias[ dep ] ? dep : result[0];            
                depMod = module[ depName ];
                mod.deps.push( depName );           
                depsData[ depName ] = true;

                if( depMod ){
                    if( depMod.status !== 4 ){               
                        // 獲取第一個(gè)重復(fù)依賴的模塊名,會(huì)在稍后進(jìn)行factorys的順序調(diào)整
                        if( !isRepeat ){
                            isRepeat = true;
                            repeatName = depName;
                        }
                        isLoaded = false;   
                    }             
                    deps.splice( i--, 1 );
                    continue;
                }
                else{
                    depMod = module[ depName ] = {};
                }  
       
                isLoaded = false;
                data.length++;
                names[ names.length++ ] = depName;
                urls[ urls.length++ ] = depMod.url = result[1];
            }
           
            // 只要當(dāng)前模塊有一個(gè)依賴模塊沒加載完就將當(dāng)前模塊的factory添加到factorys中
            if( !isLoaded ){
                factorys.unshift({
                    name : name,
                    factory : factory       
                });                   
           
                // 有重復(fù)依賴時(shí)將調(diào)整factorys的順序
                if( repeatName ){
                    repeatDepsData = data.deps[ repeatName ];
                    for( i = factorys.length - 1; i >= 0; i-- ){
                        result = factorys[i].name;
                        if( result === repeatName ){
                            pullIndex = i;                        
                            if( !repeatDepsData ){
                                break;
                            }
                        }
                       
                        if( repeatDepsData && repeatDepsData[result] ){
                            insertIndex = i;
                            break;
                        }
                    }

                    // 將重復(fù)模塊的factory插入到該模塊最后一個(gè)依賴模塊的factory后
                    factorys.splice( insertIndex + 1, 0, factorys.splice(pullIndex, 1)[0] );
                    // 將當(dāng)前模塊的factory插入到重復(fù)模塊的factory后
                    factorys.splice( insertIndex + 1, 0, factorys.shift() );
                }
            }
      
            if( names.length ){
                data.names.unshift( names );
                data.urls.unshift( urls );
            }
        }
       
        // 該模塊無依賴模塊就直接執(zhí)行其factory
        if( isLoaded ){       
            easyModule.factoryHandle( name, mod, factory, data );
        }

        easyModule.fireFactory( useKey );
       
        // 無依賴列表將刪除依賴列表的數(shù)組
        if( !mod.deps.length ){
            delete mod.deps;
        }
    };//把define方法 暴露給window
    /***
     * jQuery里這段代碼對(duì)應(yīng)
     * if ( typeof define === "function" && define.amd && define.amd.jQuery ) {
     *  define( "jquery", [], function () { return jQuery; } );
     *   }
     * ***/
    window.define.amd = {
        jQuery : true
    };

    // 初始化模塊加載器
    easyModule.init();
   
    return seedExports;
};
window.seed = seed();//定義了一個(gè)構(gòu)造函數(shù)seed(),然后暴露給全局global的屬性實(shí)例化

})( window );
 



下面是打包工具的代碼:這幾天慢慢的分析

/*
* seedCombo for seed v1.1.0
*/
var rExistId = /define\(\s*['"][^\[\('"\{]+['"]\s*,?/,
    rProtocol = /^(http(?:s)?\:\/\/|file\:.+\:\/)/,
    rModId = /([^\\\/?]+?)(\.(?:js|css))?(\?.*)?$/,    
    rRightEnd = /,?\s*(function\s*\(.*|\{.*)/,   
    rPullDeps = /((?:define|seed\.use)\(.*)/g,   
    rDeps = /((?:define|seed\.use)\([^\[\(\{]*\[)([^\]]+)/,
    rDefine = /define\(/,   
    fs = require( 'fs' ),
    path = require( 'path' ),
    depsCache = {},   
   
    seed = {
        use : function( ids ){
            return typeof ids === 'string' ? [ ids ] : ids;
        }
    }, 

    define = function( name, deps ){
        if( Array.isArray(name) ){
            deps = name;
        }
       
        return deps;    
    },   
    // 分析模塊的依賴,將依賴模塊的模塊標(biāo)識(shí)組成一個(gè)數(shù)組以便合并
    parseDeps = function( key, mods, encoding ){   
        var cache = depsCache[ key ],
            deps = [];       
        mods.forEach(function( modUrl ){
            var baseUrl = path.resolve( modUrl, '..' ),
                content, literals;         
           
            if( !~modUrl.indexOf('.js') ){
                modUrl += '.js'
            }

            // 讀取文件
            try{
                content = fs.readFileSync( modUrl, encoding );
            }
            catch( error ){
                console.log( 'Read file ' + error );
                return;
            }
           
            // 將define(), use()用正則提取出來
            literals = content.match( rPullDeps );

            literals.forEach(function( literal ){
                var arr;
                // define('hello', ['hello1'], function(){  =>  define('hello', ['hello1'])
                // use('hello', function(){  =>  use('hello')
                literal = literal.replace(rRightEnd, ')');
               
                // 然后用eval去執(zhí)行處理過的define和use獲取到依賴模塊的標(biāo)識(shí)
                arr = eval( literal );
               
                if( arr && arr.length ){               
                    // 為依賴模塊解析真實(shí)的模塊路徑
                    arr.forEach(function( item, i ){
                        arr[i] = path.resolve( baseUrl, item );
                    });
               
                    deps = deps.concat( arr );
                }
            });
        });

        if( deps.length ){           
            cache.ids = deps.concat( cache.ids );
           
            // 遞歸調(diào)用直到所有的依賴模塊都添加完
            parseDeps( key, deps, encoding );
        }
    },   
    formatDeps = function( _, define, deps ){
        var arr = deps.split( ',' ),
            len = arr.length,
            i = 0,
            item, index;
           
        for( ; i < len; i++ ){
            item = arr[i];
            item = item.replace( /['"]/g, '' ).trim();
            index = item.lastIndexOf( '/' );
            arr[i] = ~index ? item.slice( index + 1 ) : item;
        }

        return define + "'" + arr.join("','") + "'";
    },          
    // 合并內(nèi)容
    comboContent = function( key, baseUrl, encoding, format ){
        var cache = depsCache[ key ],
            unique = cache.unique,
            ids = cache.ids;
           
        ids.forEach(function( id ){
            var modName = id.match( rModId )[1],
                modUrl = path.resolve( __dirname, id ),
                content;
               
            if( !~modUrl.indexOf('.js') ){
                modUrl += '.js'
            }            
            content = fs.readFileSync( modUrl, encoding );
            // 非use()的情況下防止重復(fù)合并
            if( !~content.indexOf('use(') ){
                if( unique[modUrl] ){
                    return;
                }           
                unique[ modUrl ] = true;                
            }                     
            // utf-8 編碼格式的文件可能會(huì)有 BOM 頭,需要去掉
            if( encoding === 'UTF-8' && content.charCodeAt(0) === 0xFEFF ){
                content = content.slice( 1 );
            }           
            // 格式化
            if( typeof format === 'function' ){
                content = format( content );
            }          
            // 為匿名模塊添加模塊名
            if( !rExistId.test(content) ){ 
                content = content.replace( rDefine, "define('" + modName + "'," );
            }           
            // 格式化依賴模塊列表 ['../hello5'] => ['hello5']
            content = content.replace( rDeps, formatDeps );                      
            // 合并
            cache.contents += content + '\n';      
            console.log( 'Combo the [' + modName + '] success.' ); 
        });       
    },   
    // 寫入文件
    writeFile = function( key, mod, uglifyUrl ){
        var output = mod.output,
            contents = depsCache[ key ].contents,
            uglify, jsp, pro, ast;

        // 壓縮文件
        if( uglifyUrl ){
            uglify = require( uglifyUrl );
            jsp = uglify.parser;
            pro = uglify.uglify;                    
            ast = jsp.parse( contents );
            ast = pro.ast_mangle( ast );
            ast = pro.ast_squeeze( ast );
            contents = pro.gen_code( ast );
        }       
        // 合并好文本內(nèi)容后的回調(diào)
        if( typeof complete === 'function' ){
            contents = complete( contents );
        }                 
        // 寫入文件
        try{
            fs.writeFileSync( output, contents, mod.encoding );
        }
        catch( error ){
            console.log( 'Output file ' + error );
            return;
        }   
       
        console.log( '\n' );
        console.log( '============================================================' );
        console.log( 'Output the [' + output + '] success.' );
        console.log( '============================================================' );
        console.log( '\n' );
        delete depsCache[ key ];
    },
    seedCombo = function( options ){
        var modules = options.modules,
            baseUrl = path.resolve();       
        modules.forEach(function( mod ){
            var encoding = mod.encoding = ( mod.encoding || 'UTF-8' ).toUpperCase(),
                randomKey = ( +new Date() + '' ) + ( Math.random() + '' ).slice( -8 );
            depsCache[ randomKey ] = {
                ids : [],
                unique : {},
                contents : ''
            };             
            mod.input.forEach(function( id ){
                var modUrl = path.resolve( baseUrl, id );           
                depsCache[ randomKey ].ids.push( modUrl );
                parseDeps( randomKey, [modUrl], encoding );
            });
            comboContent( randomKey, baseUrl, encoding, mod.format );
            writeFile( randomKey, mod, options.uglifyUrl );
        });   
    };
exports.seedCombo = seedCombo;
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Node.js是目前非常火熱的技術(shù),但是它的誕生經(jīng)歷卻很奇特。 眾所周知,在Netscape設(shè)計(jì)出JavaScri...
    Myselfyan閱讀 4,191評(píng)論 2 58
  • 原文地址:Beyond The Browser: From Web Apps To Desktop Apps原文作...
    墨同學(xué)_imink閱讀 5,152評(píng)論 1 20
  • Node.js是目前非?;馃岬募夹g(shù),但是它的誕生經(jīng)歷卻很奇特。 眾所周知,在Netscape設(shè)計(jì)出JavaScri...
    w_zhuan閱讀 3,720評(píng)論 2 41
  • 周六,女生依舊準(zhǔn)時(shí)八點(diǎn)半發(fā)來了一個(gè)早安,男生一個(gè)小時(shí)后在“噩夢(mèng)”中驚醒,男生郁悶地睜開眼,清醒了一小半,摸...
    MrXi邵閱讀 267評(píng)論 1 0
  • 目錄 React Native實(shí)戰(zhàn)開發(fā)1:搭建開發(fā)環(huán)境 React Native實(shí)戰(zhàn)開發(fā)2:布局 React Na...
    Jeffrey_Hu閱讀 438評(píng)論 0 0

友情鏈接更多精彩內(nèi)容