動(dòng)手實(shí)現(xiàn)一個(gè)AMD模塊加載器(三)

在上一篇文章中晌梨,我們的AMD模塊加載器基本已經(jīng)能夠使用了中跌,但是還不夠宦焦,因?yàn)槲覀儧](méi)有允許匿名模塊发钝,以及沒(méi)有依賴(lài)等情況。實(shí)際上在amd的規(guī)范中規(guī)定的就是define函數(shù)的前兩個(gè)參數(shù)是可選的波闹,當(dāng)沒(méi)有id(模塊名)的時(shí)候也就意味著不會(huì)有模塊依賴(lài)于這個(gè)模塊酝豪。很顯然,我們的define函數(shù)的每個(gè)參數(shù)的類(lèi)型是不同的精堕,因此我們需要一些函數(shù)來(lái)做類(lèi)型判斷孵淘,如下:

  function isFun(f) {
    return Object.prototype.toString.call(f).toLowerCase().indexOf('function') > -1;
  }

  function isArr(arr) {
    return Array.isArray(arr);
  }

  function isStr(str) {
    return typeof str === 'string';
  }

將這些類(lèi)型判斷函數(shù)運(yùn)用在define函數(shù),判斷這個(gè)模塊是否有依賴(lài)歹篓,是否為匿名模塊瘫证,這是一個(gè)比較簡(jiǎn)單的工作,修改define函數(shù)如下:

  function define(name, deps, callback) {
    if(!isStr(name)) {
      callback = deps;
      deps = name;
      name = null;
    }
    
    if(!isArr(deps)) {
      callback = deps;
      deps = [];
    }

    if(moduleMap[name]) {
      name=moduleMap[name]
    } 
    name = replaceName(name);
    deps = deps.map(function(ele, i) {
      return replaceName(ele); 
    });
    
    modMap[name] = modMap[name] || {};
    modMap[name].deps = deps;
    modMap[name].status = 'loaded';
    modMap[name].callback = callback;
    modMap[name].oncomplete = modMap[name].oncomplete || [];
  }

進(jìn)行一次測(cè)試滋捶,不過(guò)在測(cè)試之前痛悯,我們需要知道的是余黎,我們將匿名模塊的name修改為了null重窟,而后面有一個(gè)replaceName方法是做name替換的,這里沒(méi)有判斷name是否為null的情況惧财,因此需要在開(kāi)頭做一次判斷巡扇,增加如下代碼:

  function replaceName(name) {
    if(name===null) {
      return name;
    }
    // ......
  }

測(cè)試代碼如下:

    loadjs.config({
      baseUrl:'./static',
      paths: {
        app: './app'
      }
    });

    loadjs.define('cc',['a'], function(a) {
      console.log(1);
      console.log(a.add(1,2));
    });

    loadjs.define('ab', function() {
      console.log('ab');
    });

    loadjs.define(function() {
      console.log('unknow');
    });

    loadjs.use(['ab','cc'],function() {
      console.log('main');
    });

測(cè)試結(jié)果如下:


image

說(shuō)明正確扭仁。此時(shí)我們的一個(gè)簡(jiǎn)單的amd模塊加載器就這樣寫(xiě)完了,刪除console增加注釋就可以比較好的使用了厅翔,最后整理一下代碼如下:


(function(root){
  var modMap = {};
  var moduleMap = {};
  var cfg = {
    baseUrl: location.href.replace(/(\/)[^\/]+$/g, function(s, s1){
      return s1
    }),
    path: {

    }
  };
  
  // 完整網(wǎng)址
  var fullPathRegExp = /^[(https?\:\/\/) | (file\:\/\/\/)]/;
  
  // 局對(duì)路徑
  var absoPathRegExp = /^\//;
  
  // 以./開(kāi)頭的相對(duì)路徑
  var relaPathRegExp = /^\.\//;
  
  // 以../開(kāi)頭的的相對(duì)路徑
  var relaPathBackRegExp = /^\.\.\//;


  function isFun(f) {
    return Object.prototype.toString.call(f).toLowerCase().indexOf('function') > -1;
  }

  function isArr(arr) {
    return Array.isArray(arr);
  }

  function isStr(str) {
    return typeof str === 'string';
  }
  
  function merge(obj1, obj2) {
    if(obj1 && obj2) {
      for(var key in obj2) {
        obj1[key] = obj2[key]
      }
    }
  }
  
  function outputPath(baseUrl, path) {
    if (relaPathRegExp.test(path)) {
      if(/\.\.\//g.test(path)) {
        var pathArr = baseUrl.split('/');
        var backPath = path.match(/\.\.\//g);
        var joinPath = path.replace(/[(^\./)|(\.\.\/)]+/g, '');
        var num = pathArr.length - backPath.length;
        return pathArr.splice(0, num).join('/').replace(/\/$/g, '') + '/' +joinPath;
      } else {
        return baseUrl.replace(/\/$/g, '') + '/' + path.replace(/[(^\./)]+/g, '');
      }
    } else if (fullPathRegExp.test(path)) {
      return path;
    } else if (absoPathRegExp.test(path)) {
      return baseUrl.replace(/\/$/g, '') + path;
    } else {
      return baseUrl.replace(/\/$/g, '') + '/' + path;
    }
  }

  function replaceName(name) {
    if(name===null) {
      return name;
    }
    if(fullPathRegExp.test(name) || absoPathRegExp.test(name) || relaPathRegExp.test(name) || relaPathBackRegExp.test(name)) {
      return outputPath(cfg.baseUrl, name);
    } else {
      var prefix = name.split('/')[0] || name;
      if(cfg.paths[prefix]) {
        if(name.split('/').length === 0) {
         return cfg.paths[prefix];
        } else {;
          var endPath = name.split('/').slice(1).join('/');
          return outputPath(cfg.paths[prefix], endPath);
        }
      } else {
        return outputPath(cfg.baseUrl, name);
      }
    }
  }

  function fixUrl(name) {
    return name.split('/')[name.split('/').length-1]
  }
  function config(obj) {
    if(obj){
     if(obj.baseUrl) {
       obj.baseUrl = outputPath(cfg.baseUrl, obj.baseUrl);
     }
     if(obj.paths) {
       var base = obj.baseUrl || cfg.baseUrl;
       for(var key in obj.paths) {
         obj.paths[key] = outputPath(base, obj.paths[key]);
       }
     }
      merge(cfg, obj);
    }
  }


  
  function use(deps, callback) {
    if(deps.length === 0) {
      callback();
    }
    var depsLength = deps.length;
    var params = [];
    
    for(var i = 0; i < deps.length; i++) {
      moduleMap[fixUrl(deps[i])] = deps[i];
      deps[i] = replaceName(deps[i]);
      (function(j){
        loadMod(deps[j], function(param) {
          depsLength--;
          params[j] = param;
          if(depsLength === 0) {
            callback.apply(null, params);
          }
        })
      })(i)
    }
    
  }

  function loadMod(name, callback) {
    /*模塊還未定義*/
    if(!modMap[name]) {
      modMap[name] = {
        status: 'loading',
        oncomplete: []
      };
      loadscript(name, function() {
        use(modMap[name].deps, function() {
          execMod(name, callback, Array.prototype.slice.call(arguments, 0));
        })
      });
    } else if(modMap[name].status === 'loading') {
      
      // 模塊正在加載
      modMap[name].oncomplete.push(callback);
    } else if (!modMap[name].exports){
      
      //模塊還未執(zhí)行完
      use(modMap[name].deps, function() {
        execMod(name, callback, Array.prototype.slice.call(arguments, 0));
      })
    }else {
      callback(modMap[name].exports);
    }
  }

  function execMod(name, callback, params) {
    var exp = modMap[name].callback.apply(null, params);
    modMap[name].exports = exp;
    callback(exp);
    execComplete(name);
  }

  function execComplete(name) {
    for(var i = 0; i < modMap[name].oncomplete.length; i++) {
      modMap[name].oncomplete[i](modMap[name].exports);
    }
  }
  
  
  function loadscript(name, callback) {
    var doc = document;
    var node = doc.createElement('script');
    node.charset = 'utf-8';
    node.src = name + '.js';
    
    /*為每個(gè)模塊添加一個(gè)隨機(jī)id*/
    node.id = 'loadjs-js-' + (Math.random() * 100).toFixed(3);
    doc.body.appendChild(node);
    node.onload = function() {
      callback();
    }
  }

  function define(name, deps, callback) {
    /*匿名模塊*/
    if(!isStr(name)) {
      callback = deps;
      deps = name;
      name = null;
    }

    /*沒(méi)有依賴(lài)*/
    if(!isArr(deps)) {
      callback = deps;
      deps = [];
    }

    if(moduleMap[name]) {
      name=moduleMap[name]
    } 
    
    name = replaceName(name);
    
    /*對(duì)每個(gè)依賴(lài)名進(jìn)行路徑替換*/
    deps = deps.map(function(ele, i) {
      return replaceName(ele); 
    });

    modMap[name] = modMap[name] || {};
    modMap[name].deps = deps;
    modMap[name].status = 'loaded';
    modMap[name].callback = callback;
    modMap[name].oncomplete = modMap[name].oncomplete || [];
  }

  var loadjs = {
    define: define,
    use: use,
    config: config
  };

  root.define = define;
  root.loadjs = loadjs;
  root.modMap = modMap;
})(window);

系列文章:
動(dòng)手實(shí)現(xiàn)一個(gè)AMD模塊加載器(一)
動(dòng)手實(shí)現(xiàn)一個(gè)AMD模塊加載器(二)
動(dòng)手實(shí)現(xiàn)一個(gè)AMD模塊加載器(三)

最后是一個(gè)廣告貼乖坠,最近新開(kāi)了一個(gè)分享技術(shù)的公眾號(hào),歡迎大家關(guān)注??

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末刀闷,一起剝皮案震驚了整個(gè)濱河市熊泵,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌甸昏,老刑警劉巖顽分,帶你破解...
    沈念sama閱讀 217,907評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異施蜜,居然都是意外死亡卒蘸,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,987評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)翻默,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)缸沃,“玉大人,你說(shuō)我怎么就攤上這事修械≈耗粒” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,298評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵祠肥,是天一觀的道長(zhǎng)武氓。 經(jīng)常有香客問(wèn)我,道長(zhǎng)仇箱,這世上最難降的妖魔是什么县恕? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,586評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮剂桥,結(jié)果婚禮上忠烛,老公的妹妹穿的比我還像新娘。我一直安慰自己权逗,他們只是感情好美尸,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,633評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著斟薇,像睡著了一般师坎。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上堪滨,一...
    開(kāi)封第一講書(shū)人閱讀 51,488評(píng)論 1 302
  • 那天胯陋,我揣著相機(jī)與錄音,去河邊找鬼。 笑死遏乔,一個(gè)胖子當(dāng)著我的面吹牛义矛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播盟萨,決...
    沈念sama閱讀 40,275評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼凉翻,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了捻激?” 一聲冷哼從身側(cè)響起制轰,我...
    開(kāi)封第一講書(shū)人閱讀 39,176評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎胞谭,沒(méi)想到半個(gè)月后艇挨,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,619評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡韭赘,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,819評(píng)論 3 336
  • 正文 我和宋清朗相戀三年缩滨,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片泉瞻。...
    茶點(diǎn)故事閱讀 39,932評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡脉漏,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出袖牙,到底是詐尸還是另有隱情侧巨,我是刑警寧澤,帶...
    沈念sama閱讀 35,655評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響蜡峰,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜坦仍,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,265評(píng)論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望叨襟。 院中可真熱鬧繁扎,春花似錦、人聲如沸糊闽。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,871評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)右犹。三九已至提澎,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間念链,已是汗流浹背盼忌。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,994評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工莉炉, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人碴犬。 一個(gè)月前我還...
    沈念sama閱讀 48,095評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像梆暮,于是被迫代替她去往敵國(guó)和親服协。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,884評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • 在上一篇文章中啦粹,我們已經(jīng)基本完成了模塊加載器的基本功能偿荷,接下來(lái)來(lái)完成一下路徑解析的問(wèn)題。 在之前的功能中唠椭,我們所有...
    忽如寄閱讀 199評(píng)論 0 1
  • 1.幾種基本數(shù)據(jù)類(lèi)型?復(fù)雜數(shù)據(jù)類(lèi)型?值類(lèi)型和引用數(shù)據(jù)類(lèi)型?堆棧數(shù)據(jù)結(jié)構(gòu)? 基本數(shù)據(jù)類(lèi)型:Undefined跳纳、Nul...
    極樂(lè)君閱讀 5,517評(píng)論 0 106
  • ## 前言 The Module Pattern,模塊模式贪嫂,也譯為模組模式寺庄,是一種通用的對(duì)代碼進(jìn)行模塊化組織與定義...
    b2e16cc43137閱讀 3,028評(píng)論 1 4
  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)力崇,斷路器斗塘,智...
    卡卡羅2017閱讀 134,656評(píng)論 18 139
  • 我有一個(gè)朋友S,她是個(gè)韓國(guó)人搓侄,從小在中國(guó)生活瞄桨。家庭背景不錯(cuò),爸爸媽媽都在政府里辦事讶踪,自己本身也相貌出眾讲婚,才華橫溢,...
    Jessie_JX閱讀 335評(píng)論 0 2