寫一個適應(yīng)所有環(huán)境的JS模塊

背景

在ES6以前猾愿,JS語言沒有模塊化,如何讓JS不止運行在瀏覽器德玫,且能更有效的管理代碼匪蟀,于是應(yīng)運而生CommonJS這種規(guī)范,定義了三個全局變量:

require宰僧,exports材彪,module

require 用于引入一個模塊
exports 對外暴露模塊的接口,可以是任何類型
module 是這個模塊本身的對象
require引入時獲取的是這個模塊對外暴露的接口(exports
Node.js 使用了CommonJS規(guī)范:

var foo = require('foo');

var out = foo.bar();

module.exports = out;

在瀏覽器端琴儿,不像Node.js內(nèi)部支持CommonJS段化,如何進(jìn)行模塊化,
于是出現(xiàn)了 CMD 與 AMD 兩種方式造成,其主要代表是 seajs 和 requirejs显熏,
他們都定義了一個全局函數(shù) define 來創(chuàng)建一個模塊:

// CMD
define(function(require, exports, module) {
  var foo = require('foo');
  var out = foo.bar();
  module.exports = out;
})

// AMD
define(['foo'], function(foo) {
  var out = foo.bar();
  return out;
});

可以看出CMD完好的保留了CommonJS的風(fēng)格,
而AMD用了一種更簡潔的依賴注入和函數(shù)返回的方式實現(xiàn)模塊化晒屎。
兩者除風(fēng)格不同外最大區(qū)別在于加載依賴模塊的方式喘蟆,
CMD是懶加載,在require時才會加載依賴鼓鲁,
而AMD是預(yù)加載蕴轨,在定義模塊時就提前加載好所有依賴。
各有千秋骇吭,各有適合的場景橙弱,網(wǎng)上有兩者詳細(xì)評測和激烈的討論。

正題

我們要實現(xiàn)一個模塊燥狰,讓它既能在seajs(CMD)環(huán)境里引入棘脐,又能在requirejs(AMD)環(huán)境中引入,
當(dāng)然也能在Node.js(CommonJS)中使用龙致,另外還可以在沒有模塊化的環(huán)境中用script標(biāo)簽全局引入蛀缝,
可謂是對write once,run anywhere的向往目代,實際上大部分npm的前端組件包也要考慮這個屈梁。

  • 首先一個模塊看起來應(yīng)該是這樣:
var moduleName = {};
return moduleName;

當(dāng)然,模塊輸出的不止可以是對象像啼,還是可以是任何值,包括一個類潭苞。

  • 分析CMD和AMD忽冻,我們需要提供一個工廠函數(shù)傳入define來定義模塊,所以變成這樣:
function factory () {
  var moduleName = {};
  return moduleName;
}
  • 為適應(yīng)Node.js此疹,可以來判斷全局變量僧诚,由于require在CMD和ADM中都有定義遮婶,所以只判斷:
typeof module !== 'undefined' && typeof exports === 'object'

于是變成這樣:

function factory () {
  var moduleName = {};
  return moduleName;
}

if (typeof module !== 'undefined' && typeof exports === 'object') {
  module.exports = factory()
}

至此已經(jīng)能夠滿足Node.js的需求。

  • 當(dāng)沒有上述全局變量湖笨,且有define全局變量時旗扑,我們認(rèn)為是AMD或CMD,可以直接將factory傳入define:
function factory () {
  var moduleName = {};
  return moduleName;
}

if (typeof module !== 'undefined' && typeof exports === 'object') {
  module.exports = factory()
} else if (typeof define === 'function' && (define.cmd || define.amd)) {
  define(factory)
}

注意:CMD其實也支持return返回模塊接口慈省,所以兩者可以通用臀防。

  • 最后是script標(biāo)簽全局引入,我們可以將模塊放在window上边败,

為了模塊內(nèi)部在瀏覽器和Node.js中都能使用全局對象袱衷,我們可以做此判斷:

var global = typeof window !== 'undefined' ? window : global;

同時,我們用一個立刻執(zhí)行的閉包函數(shù)將所有代碼包含笑窜,來避免污染全局空間致燥,
并將global對象傳入閉包函數(shù),最終變成這樣:

;(function (global) {
  function factory () {
    var moduleName = {};
    return moduleName;
  }
  
  if (typeof module !== 'undefined' && typeof exports === 'object') {
    module.exports = factory()
  } else if (typeof define === 'function' && (define.cmd || define.amd)) {
    define(factory)
  } else {
    global.moduleName = factory();
  }
})(typeof window !== 'undefined' ? window : global);

注意:閉包前加上分號是為了給前一個模塊填坑排截,分號多了沒問題嫌蚤,少了則語句可能發(fā)生變化。

  • 我們參考一下Vuex的源碼:
;(function (global, factory) {
  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
    typeof define === 'function' && define.amd ? define(factory) :
      (global.Vuex = factory());
}(this, (function () {
  'use strict';
  // ……
  var index = {
    Store: Store,
    install: install,
    version: '2.5.0',
    mapState: mapState,
    mapMutations: mapMutations,
    mapGetters: mapGetters,
    mapActions: mapActions,
    createNamespacedHelpers: createNamespacedHelpers
  };
  return index;
})));

這里有兩個變化:
函數(shù)factory以匿名函數(shù)的方式引入断傲,結(jié)構(gòu)從;()(); 變成 ;(()());脱吱;
this代替了typeof window !== 'undefined' ? window : globalthis在瀏覽器是window艳悔,在Node中是golbal急凰,很是精妙。

  • 稍微簡化一下:
;(function (global) {
  function factory () {
    var index= {};
    return index;
  }
  
  typeof module !== 'undefined' && typeof exports === 'object' ? module.exports = factory() :
  typeof define === 'function' && (define.cmd || define.amd) ? define(factory) :
  (global.moduleName = factory());
})(this);

或者

;(function (flobal, factory) {
  typeof module !== 'undefined' && typeof exports === 'object' ? module.exports = factory() :
  typeof define === 'function' && (define.cmd || define.amd) ? define(factory) :
  (global.moduleName = factory());
}(this, (function () {
    var index = {};
    return index;
  }
)));
  • 于是同一個js文件我們能愉快的在不同環(huán)境這樣引入:
// Node.js
var myModule = require('moduleName');

// Seajs
define(function (require, exports, module) {
  var myModule = require('moduleName');
})

// Requirejs
define(['moduleName'], function (moduleName) {
})

// Browser global
<script src="moduleName.js"></script>

感謝瀏覽猜年,歡迎評論指正抡锈,轉(zhuǎn)載請標(biāo)明出處。
參考博文:http://www.cnblogs.com/brandonchen/p/5550470.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末乔外,一起剝皮案震驚了整個濱河市床三,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌杨幼,老刑警劉巖撇簿,帶你破解...
    沈念sama閱讀 210,914評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異差购,居然都是意外死亡四瘫,警方通過查閱死者的電腦和手機(jī)欲逃,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評論 2 383
  • 文/潘曉璐 我一進(jìn)店門找蜜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人稳析,你說我怎么就攤上這事洗做」眩” “怎么了?”我有些...
    開封第一講書人閱讀 156,531評論 0 345
  • 文/不壞的土叔 我叫張陵诚纸,是天一觀的道長撰筷。 經(jīng)常有香客問我,道長畦徘,這世上最難降的妖魔是什么毕籽? 我笑而不...
    開封第一講書人閱讀 56,309評論 1 282
  • 正文 為了忘掉前任,我火速辦了婚禮旧烧,結(jié)果婚禮上影钉,老公的妹妹穿的比我還像新娘。我一直安慰自己掘剪,他們只是感情好平委,可當(dāng)我...
    茶點故事閱讀 65,381評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著夺谁,像睡著了一般廉赔。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上匾鸥,一...
    開封第一講書人閱讀 49,730評論 1 289
  • 那天蜡塌,我揣著相機(jī)與錄音,去河邊找鬼勿负。 笑死馏艾,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的奴愉。 我是一名探鬼主播琅摩,決...
    沈念sama閱讀 38,882評論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼锭硼!你這毒婦竟也來了房资?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,643評論 0 266
  • 序言:老撾萬榮一對情侶失蹤檀头,失蹤者是張志新(化名)和其女友劉穎轰异,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體暑始,經(jīng)...
    沈念sama閱讀 44,095評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡搭独,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,448評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了廊镜。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片牙肝。...
    茶點故事閱讀 38,566評論 1 339
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出惊奇,到底是詐尸還是另有隱情,我是刑警寧澤播赁,帶...
    沈念sama閱讀 34,253評論 4 328
  • 正文 年R本政府宣布颂郎,位于F島的核電站,受9級特大地震影響容为,放射性物質(zhì)發(fā)生泄漏乓序。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,829評論 3 312
  • 文/蒙蒙 一坎背、第九天 我趴在偏房一處隱蔽的房頂上張望替劈。 院中可真熱鬧,春花似錦得滤、人聲如沸陨献。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,715評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽眨业。三九已至,卻和暖如春沮协,著一層夾襖步出監(jiān)牢的瞬間龄捡,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,945評論 1 264
  • 我被黑心中介騙來泰國打工慷暂, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留聘殖,地道東北人。 一個月前我還...
    沈念sama閱讀 46,248評論 2 360
  • 正文 我出身青樓行瑞,卻偏偏與公主長得像奸腺,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子蘑辑,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,440評論 2 348

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

  • JS中的模塊規(guī)范(CommonJS洋魂,AMD绷旗,CMD),如果你聽過js模塊化這個東西副砍,那么你就應(yīng)該聽過或Common...
    小蝦米前端閱讀 4,386評論 0 12
  • 什么是模塊化開發(fā)衔肢? 前端開發(fā)中,起初只要在script標(biāo)簽中嵌入幾十上百行代碼就能實現(xiàn)一些基本的交互效果豁翎,后來js...
    半世韶華憶闌珊閱讀 651評論 0 0
  • 隨著前端業(yè)務(wù)復(fù)雜度的增加背桐,模塊化成為一個大的趨勢。而在ES6還未被瀏覽器所支持的情況下蝉揍,commonjs作為ES6...
    吳高亮閱讀 1,052評論 0 3
  • 1 Node.js模塊的實現(xiàn)# 之前在網(wǎng)上查閱了許多介紹Node.js的文章,可惜對于Node.js的模塊機(jī)制大都...
    七寸知架構(gòu)閱讀 2,054評論 1 50
  • 正式進(jìn)入了隆冬時節(jié)链峭,屋里聽到了凌冽的寒風(fēng),想想出門后刺骨的風(fēng)像刀子一樣刮著臉 又沾,有想過拒絕出門弊仪。可是,現(xiàn)在不可以像...
    白蘇打閱讀 343評論 4 0