前端的模塊化根據(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
}
})
最后涣仿,模塊化的目的是為了代碼的更好的復用,所以咯变过,使用方式可以各式各樣,但是目的一致涝涤。