一、什么是模塊化?
模塊化是指在解決某一個(gè)復(fù)雜問題或者一系列的雜糅問題時(shí),依照一種分類的思維把問題進(jìn)行系統(tǒng)性的分解以之處理。模塊化是一種處理復(fù)雜系統(tǒng)分解為代碼結(jié)構(gòu)更合理,可維護(hù)性更高的可管理的模塊的方式??梢韵胂笠粋€(gè)巨大的系統(tǒng)代碼,被整合優(yōu)化分割成邏輯性很強(qiáng)的模塊時(shí),對(duì)于軟件是一種何等意義的存在。對(duì)于軟件行業(yè)來(lái)說:解耦軟件系統(tǒng)的復(fù)雜性,使得不管多么大的系統(tǒng),也可以將管理,開發(fā),維護(hù)變得“有理可循”。
另一些對(duì)于模塊化一些專業(yè)的定義為:模塊化是軟件系統(tǒng)的屬性,這個(gè)系統(tǒng)被分解為一組高內(nèi)聚,低耦合的模塊。那么在理想狀態(tài)下我們只需要完成自己部分的核心業(yè)務(wù)邏輯代碼,其他方面的依賴可以通過直接加載被人已經(jīng)寫好模塊進(jìn)行使用即可。
既然是模塊化設(shè)計(jì),那么作為一個(gè)模塊化系統(tǒng)所必須的能力:
- 定義封裝的模塊;
- 定義新模塊對(duì)其他模塊的依賴;
- 可對(duì)其他模塊的引入支持;
本篇,將市面上流行的幾種模塊化技術(shù)進(jìn)行對(duì)比,讓大家清楚具體的規(guī)范:
- CommonJS(注意:不要與CMD混淆)
- AMD(Asyncronous Module Definition)異步模塊定義
- CMD(Common Module Definition)通用模塊定義
- UMD(Universal Module Definition)通用模塊定義
- ES6 module
二、規(guī)范
2.1、CommonJS
- 特點(diǎn):
- 一個(gè)單獨(dú)的文件就是一個(gè)模塊;
- 可以多次加載,但只會(huì)在第一次加載時(shí)運(yùn)行一次,運(yùn)行結(jié)果被緩存;
- 以后再加載,就直接讀取緩存結(jié)果;
- 要想讓模塊再次運(yùn)行,必須清除緩存;
- 模塊加載會(huì)阻塞后續(xù)代碼執(zhí)行,需要等到模塊加載完成才能繼續(xù)執(zhí)行(同步加載);
- 環(huán)境:服務(wù)器環(huán)境
- 應(yīng)用:nodejs的模塊規(guī)范是參照commonJS實(shí)現(xiàn)的
- 語(yǔ)法:
- 導(dǎo)入:require('路徑')
- 導(dǎo)出:module.exports和exports
- 注(module.exports和exports的區(qū)別):
- exports只是module.exports的一個(gè)引用
- 相當(dāng)于Node為每個(gè)模塊提供一個(gè)exports變量,指向module.exports
- 同在每個(gè)模塊頭部,有一行var exports = module.exports;
// a.js
// 相當(dāng)于這里還有一行:var exports = module.exports;代碼
exports.a = 'Hello world'; // 相當(dāng)于:module.exports.a = 'Hello world';
// b.js
var moduleA = require('./a.js');
console.log(moduleA.a); // 打印出:hello world
---------------------------------------------------------------------------------
// 稍微復(fù)雜點(diǎn)的例子
var $ = require('jquery');
var _ = require('underscore');
// methods
function a(){}; // private because it's omitted from module.exports
function b(){}; // public because it's defined in module.exports
function c(){}; // public because it's defined in module.exports
// exposed public methods
module.exports = {
b: b,
c: c
};
2.2、AMD
- 特點(diǎn):
- 異步加載
- 管理模塊之間的依賴性(多個(gè)JS文件依賴時(shí),依次加載最早被依賴的JS文件)
- 環(huán)境:瀏覽器環(huán)境
- 應(yīng)用:requireJS是參照AMD規(guī)范實(shí)現(xiàn)的
- 語(yǔ)法:
- 導(dǎo)入:require( ['模塊名稱'], function (模塊變量引用){ 代碼 } );
- 導(dǎo)出:define(function (){return '值');
// a.js
define(function (){
return {
a:'hello world'
}
});
// b.js
require(['./a.js'], function (moduleA){
console.log(moduleA.a); // 打印出:hello world
});
---------------------------------------------------------------------------------
// 稍微復(fù)雜點(diǎn)的例子
define(['jquery', 'underscore'], function ($, _) {
// methods
function a(){}; // private because it's not returned
function b(){}; // public because it's returned
function c(){}; // public because it's returned
// exposed public methods
return {
b: b,
c: c
}
});
2.3、CMD
- 特點(diǎn):
- CMD是在AMD基礎(chǔ)上改進(jìn)的一種規(guī)范;
- 與AMD不同在于對(duì)依賴模塊的執(zhí)行時(shí)機(jī)處理不同:
- CMD是就近依賴
- AMD是前置依賴
- 環(huán)境:瀏覽器環(huán)境
- 應(yīng)用:seaJS是參照CMD規(guī)范實(shí)現(xiàn)的;requireJS最新幾個(gè)版本也參照了CMD規(guī)范;
- 語(yǔ)法:
- 導(dǎo)入:define(function(require, exports, module) {});
- 導(dǎo)出:define(function (){return '值');
// a.js
define(function (require, exports, module){
exports.a = 'hello world';
});
// b.js
define(function (require, exports, module){
var moduleA = require('./a.js');
console.log(moduleA.a); // 打印出:hello world
});
2.4、UMD
- 特點(diǎn):
- 兼容AMD和CommonJS規(guī)范的同時(shí),還兼容全局引用的方式;
- 環(huán)境:瀏覽器環(huán)境
- 應(yīng)用:無(wú)
- 語(yǔ)法:無(wú)
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD
define(['jquery'], factory);
} else if (typeof exports === 'object') {
// Node, CommonJS之類的
module.exports = factory(require('jquery'));
} else {
// 瀏覽器全局變量(root 即 window)
root.returnExports = factory(root.jQuery);
}
}(this, function ($) {
//方法
function myFunc(){};
//暴露公共方法
return myFunc;
}));
---------------------------------------------------------------------------------
// 稍微復(fù)雜點(diǎn)的例子
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD
define(['jquery', 'underscore'], factory);
} else if (typeof exports === 'object') {
// Node, CommonJS-like
module.exports = factory(require('jquery'), require('underscore'));
} else {
// Browser globals (root is window)
root.returnExports = factory(root.jQuery, root._);
}
}(this, function ($, _) {
// methods
function a(){}; // private because it's not returned
function b(){}; // public because it's returned
function c(){}; // public because it's returned
// exposed public methods
return {
b: b,
c: c
}
}));
2.5、ES6 module
- 特點(diǎn):
- 按需加載(編譯時(shí)加載)
- import和export命令只能在模塊的頂層,不能在代碼塊之中
- 環(huán)境:瀏覽器環(huán)境
- 應(yīng)用:ES6最新語(yǔ)法支持規(guī)范;
- 語(yǔ)法:
- 導(dǎo)入:import {模塊名A,模塊名B...} from '模塊路徑';
- 導(dǎo)出:export和export default
- import('模塊路徑').then()方法
- 注意:
- export只支持對(duì)象形式導(dǎo)出,不支持值的導(dǎo)出
- export default命令用于指定模塊的默認(rèn)輸出,只支持值導(dǎo)出
- export default只能指定一個(gè),本質(zhì)上它就是輸出一個(gè)叫做default的變量或方法
/*錯(cuò)誤的寫法*/
// 寫法一
export 1;
// 寫法二
var m = 1;
export m;
// 寫法三
if (x === 2) {
import MyModual from './myModual';
}
/*正確的三種寫法*/
// 寫法一
export var m = 1;
// 寫法二
var m = 1;
export {m};
// 寫法三
var n = 1;
export {n as m};
// 寫法四
var n = 1;
export default n;
// 寫法五
if (true) {
import('./myModule.js')
.then(({export1, export2}) => {
// ...·
});
}
// 寫法六
Promise.all([
import('./module1.js'),
import('./module2.js'),
import('./module3.js'),
])
.then(([module1, module2, module3]) => {
···
});