搞懂前端模塊化的演進

前端的模塊化根據(jù)環(huán)境的不同有不同的規(guī)范,所以寫一下各個方面的前端的模塊化畏鼓,以加深自己的理解酱酬。主要分四塊:es5環(huán)境下的模塊化云矫、commonJs下的模塊化、es6環(huán)境下的模塊化让禀、AMD和CMD挑社。

一巡揍、es5 環(huán)境下的模塊化

早期的 Javascript 并沒有模塊化的思想,他被創(chuàng)造出來作為腳本的存在 腮敌,用來解決HTML中的一些基本的動畫和展示阱当。后來隨著前后端分離,前端業(yè)務(wù)量的增加弊添,以及 命名沖突录淡、代碼無法復用等問題的出現(xiàn)油坝,對于Javascript有了新的要求。

其實es5也可以實現(xiàn)簡單的模塊化澈圈。主要方式是:閉包+變量

// moduleA.js
;
var moduleA = (function(){
    var obj = {
        name:'one',
        add: function(num1, num2) {
            return num1 + num2
        }
    }
    
    return obj
})()

// main.js
var name = moduleA.name
var num = moduleA.add(1, 2)

// 在HTML 中需要注意兩個js引入順序彼水, 先module.js 后 main.js

二极舔、commonJs下的模塊化

commonJs規(guī)范 最常見的應用就是nodejs 環(huán)境中的應用。它定義了導入和導出的方式:

  • require()
  • module.exports / exports

module.exports 的導出拆魏,導出為一個對象盯桦,可以在里面定義導出的屬性渤刃。

// moduleA.js
var name = 'one'
function add(num1, num2) {
    return num1 + num2
}

// 這里的導出必定是一個對象
module.exports = {
    name, // => num: num
    add // => add: add
}

exports 簡寫導出拥峦,注意不能對exports 進行賦值操作卖子。

// moduleB.js
// 使用exports的時候可以通過點的形式添加屬性,最終他們會被添加到一個對象中對外導出洋闽。
exports.name = 'moduleB'
exports.mul = function(num1, num2) {
    return num1 * num2
}

// 錯誤用法 如下
exports = {
    name : 'moduleB'
}
/*
// 規(guī)范規(guī)定
var exports = module.exports

exports.name = 'ddd' // 相當于在module.exports.name = 'ddd'
exports = {
    name : 'moduleB'
} // 這里進行了重新賦值 此時就和module.export無關(guān)了

// 規(guī)范規(guī)定最后的返回是
return module.exports
*/

require()` 導入使用玄柠,動態(tài)加載诫舅, 需要注意導入的位置,同步方法刊懈,讀入并執(zhí)行一個js文件(如果已經(jīng)讀入了就從緩存加載)这弧,然后返回該文件導出的對象虚汛,如果沒有找到,則會報錯卷哩。

// main.js
//  導入 可以放在文件的最開頭
var moduleA = require('./moduleA.js')

// 導入 可以放在方法中 蛋辈,這個時候什么時候方法被調(diào)用殉疼,什么時候?qū)氲奈募艜患虞d進入內(nèi)存
function foo() {
    var { name, mul } = require('./moduleB.js') // 當然 此時可以解構(gòu)導入的對象
}

require的加載規(guī)則:

  • require(/usr/test/a)/開頭的路徑梯浪,會作為全局路徑加載
  • require(./b).開頭的路徑,會作為相對路徑加載
  • require('path') 不以/ 或者.開頭挂洛,會從node_module中加載礼预。
  • require('test_module/path/a') 不以/ 或者.開頭虏劲,是一個路徑,那么會先找到test_module的位置然后往下找柒巫。
  • 如果指定 的模塊沒找到励堡,Node會嘗試為文件名添加.js堡掏、.json.node后泉唁,再去搜索鹅龄。
  • 可以通過require.resolve() 得到文件確切加載名亭畜。

require的加載原理:

  • require 不是一個全局命令扮休,它指向了模塊的module.require命令
  • module.require 調(diào)用了Module._load函數(shù)
  • Module._load函數(shù)內(nèi)部會進行一下判斷
    • 檢查Module._cache,緩存中是否有指定模塊
    • 如果沒有就創(chuàng)建一個新的Module 實例拴鸵,并保存到緩存
    • 調(diào)用Module.load()加載指定的模塊文件。讀取內(nèi)容后調(diào)用Module.compile()進行解析
    • 解析/加載過程中如果報錯劲藐,就從緩存中刪除
    • 返回模塊對應的module.exports

三八堡、es6環(huán)境下的模塊化

es6的模塊化同樣也是分導入和導出兩塊瘩燥,同時為了使得瀏覽器識別用了模塊化,需要在腳本引用的時候標明厉膀。

<html>
    <body>
        ...
        /* 只有在這里標明type, 在js文件中使用es6模塊化二拐,才能被瀏覽器處理 */
        <script src="./module.js" type="module"></script>
        <script src="./main.js" type="module"></script>
    </body>
</html>

具體使用方式:

  • export
  • import from

export 導出模塊,主要用法:

// moduleA.js

const name = 'moduleA'
function add(a, b) {
    return a + b
}

// 1. 導出一個對象(推薦)
export {
  name, add
}

// 2. 單個導出
export const age = 21
export function mul(a, b) {
    return a * b
}

// 3. 在1基礎(chǔ)上改進  重定義對外暴露的名稱
export {
  name as outName, 
  add as OutAdd
}

// 4. 默認導出 同一個文件中只允許export default 1個
// 好處是:這種方式導出百新, 在導入的時候可以自由命名企软。
// export default name
// export default add
// export default { }

import from 靜態(tài)導入模塊饭望,主要用法:

// main.js
// 導入一般寫在文件開頭 靜態(tài)導入
// 1. 導入模塊
import  {name, add} from './moduleA.js'

// 2. 重命名導入
import  {name as outName, add as outAdd} from './moduleA.js'

// 3. 當導出使用的是export.default的時候形庭,可以如下導入
import data from './moduleA.js'

// 4. 統(tǒng)一全部導入
import * as info from './moduleA.js'

// 5. 還可以通過import() 函數(shù)導入
import('./moduleA.js').then(res =>  {
    
}).catch(err => {})

四、AMD厌漂、CMD

AMD作為一種模塊規(guī)范,采用的是異步的方式去加載依賴的模塊苇倡,比如:require.js實現(xiàn)AMD

// 使用define方法定義一個模塊
define('a', [/*  需要引用的模塊 */], function () {
    return 'a';
});
require([/*  需要引用的模塊 */], function () {
 
});

CMD是另外一種模塊規(guī)范富纸,和AMD 類似旨椒,比如:sea.js 兩者的區(qū)別是:

  • AMD 事先聲明,前置加載
  • CMD 動態(tài)生命综慎,用到時加載
define(function(require, exports, module) {
    // 懶加載
    var $ = require('jquery.js')
    exports.add = function(a, b) {
        return a+b
    }
})

最后涣仿,模塊化的目的是為了代碼的更好的復用,所以咯变过,使用方式可以各式各樣,但是目的一致涝涤。

以上是我對前端模塊化的理解媚狰,如果能夠幫到您阔拳,希望不吝點個贊哦;如果哪里寫的不對糊肠,還望告知~~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末辨宠,一起剝皮案震驚了整個濱河市货裹,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌弧圆,老刑警劉巖赋兵,帶你破解...
    沈念sama閱讀 217,406評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異霹期,居然都是意外死亡,警方通過查閱死者的電腦和手機拯田,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評論 3 393
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人吭产,你說我怎么就攤上這事侣监。” “怎么了臣淤?”我有些...
    開封第一講書人閱讀 163,711評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長荒典。 經(jīng)常有香客問我酪劫,道長,這世上最難降的妖魔是什么寺董? 我笑而不...
    開封第一講書人閱讀 58,380評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮遮咖,結(jié)果婚禮上滩字,老公的妹妹穿的比我還像新娘御吞。我一直安慰自己,他們只是感情好陶珠,可當我...
    茶點故事閱讀 67,432評論 6 392
  • 文/花漫 我一把揭開白布挟裂。 她就那樣靜靜地躺著揍诽,像睡著了一般。 火紅的嫁衣襯著肌膚如雪暑脆。 梳的紋絲不亂的頭發(fā)上渠啤,一...
    開封第一講書人閱讀 51,301評論 1 301
  • 那天添吗,我揣著相機與錄音沥曹,去河邊找鬼。 笑死妓美,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的玄帕。 我是一名探鬼主播,決...
    沈念sama閱讀 40,145評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼裤纹,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起锡移,我...
    開封第一講書人閱讀 39,008評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎漆际,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體奸汇,經(jīng)...
    沈念sama閱讀 45,443評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡施符,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,649評論 3 334
  • 正文 我和宋清朗相戀三年擂找,在試婚紗的時候發(fā)現(xiàn)自己被綠了戳吝。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片贯涎。...
    茶點故事閱讀 39,795評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖塘雳,靈堂內(nèi)的尸體忽然破棺而出陆盘,到底是詐尸還是另有隱情败明,我是刑警寧澤隘马,帶...
    沈念sama閱讀 35,501評論 5 345
  • 正文 年R本政府宣布妻顶,位于F島的核電站,受9級特大地震影響盈包,放射性物質(zhì)發(fā)生泄漏沸呐。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,119評論 3 328
  • 文/蒙蒙 一崭添、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧叛氨,春花似錦呼渣、人聲如沸寞埠。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蓝角。三九已至阱穗,卻和暖如春使鹅,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背患朱。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評論 1 269
  • 我被黑心中介騙來泰國打工鲁僚, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留裁厅,地道東北人冰沙。 一個月前我還...
    沈念sama閱讀 47,899評論 2 370
  • 正文 我出身青樓执虹,卻偏偏與公主長得像拓挥,于是被迫代替她去往敵國和親声畏。 傳聞我的和親對象是個殘疾皇子撞叽,可洞房花燭夜當晚...
    茶點故事閱讀 44,724評論 2 354

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