參考探索js的模塊化
什么是JavaScript模塊化?
模塊化在我看來,就是把一些公共的函數(shù)封裝起來給其他地方調(diào)用,而不用重復去寫一些冗余的函數(shù)代碼。
JavaScript模塊化大致發(fā)展過程
CommonJS(服務(wù)端) => AMD (瀏覽器端) => CMD / UMD => ES Module
CommonJS
CommonJS主要用于服務(wù)器端。 這個規(guī)范是同步的
特點:
模塊可以多次加載,首次加載的結(jié)果將會被緩存,想讓模塊重新運行需要清除緩存。
-
模塊的加載是一項阻塞操作,也就是同步加載。
// a.js module.exports = { moduleFunc: function() { return true; }; } // 或 exports.moduleFunc = function() { return true; }; // 在 b.js 中引用 var moduleA = require('a.js'); // 或 var moduleFunc = require('a.js').moduleFunc; console.log(moduleA.moduleFunc()); console.log(moduleFunc())
AMD規(guī)范
在commonJS中, 模塊的加載過程是一個同步的過程, 很明顯地,如果在瀏覽器端, 肯定就會引起瀏覽器頁面的阻塞,因此,這時候?qū)δK異步加載的需求就出現(xiàn)了。從而出現(xiàn)AMD規(guī)范
異步模塊定義規(guī)范(AMD)制定了定義模塊的規(guī)則,這樣模塊和模塊的依賴可以被異步加載。這和瀏覽器的異步加載模塊的環(huán)境剛好適應(yīng)(瀏覽器同步加載模塊會導致性能、可用性、調(diào)試和跨域訪問等問題)
這時候RequireJS應(yīng)運而生
RequireJS
特點:
前置依賴,異步加載
-
便于管理模塊之間的依賴性,有利于代碼的編寫和維護。
// a.js define(function (require, exports, module) { console.log('a.js'); exports.name = 'Jack'; }); // b.js define(function (require, exports, module) { console.log('b.js'); exports.desc = 'Hello World'; }); // main.js require(['a', 'b'], function (moduleA, moduleB) { console.log('main.js'); console.log(moduleA.name + ', ' + moduleB.desc); }); // 執(zhí)行順序: // a.js // b.js // main.js
然而, 他也有他的不足之處
按照 AMD 的規(guī)范,在定義模塊的時候需要把所有依賴模塊都羅列一遍(前置依賴),而且在使用時還需要在 factory 中作為形參傳進去。
define(['a', 'b', 'c', 'd', 'e', 'f', 'g'], function(a, b, c, d, e, f, g){ ..... });
是不是看起來又丑又復雜。。
RequireJS 模塊化的順序是這樣的:模塊預加載 => 全部模塊預執(zhí)行 => 主邏輯中調(diào)用模塊,所以實質(zhì)是依賴加載完成后還會預先一一將模塊執(zhí)行一遍,這種方式會使得程序效率有點低。
這個時候就出現(xiàn)了CMD規(guī)范,典型的就是seajs模塊化
SeaJS
SeaJS 模塊化的順序是這樣的:模塊預加載 => 主邏輯調(diào)用模塊前才執(zhí)行模塊中的代碼,通過依賴的延遲執(zhí)行,很好解決了 RequireJS 被詬病的缺點。
// a.js
define(function (require, exports, module) {
console.log('a.js');
exports.name = 'Jack';
});
// main.js
define(function (require, exports, module) {
console.log('main.js');
var moduleA = require('a');
console.log(moduleA.name);
});
// 執(zhí)行順序
// main.js
// a.js
ES6的module
ES Module 的思想是盡量的靜態(tài)化,即在編譯時就確定所有模塊的依賴關(guān)系,以及輸入和輸出的變量,和 CommonJS 和 AMD/CMD 這些標準不同的是,它們都是在運行時才能確定需要依賴哪一些模塊并且執(zhí)行它。ES Module 使得靜態(tài)分析成為可能
模塊功能主要由兩個命令構(gòu)成:export 和 import。export 命令用于規(guī)定模塊的對外接口,import 命令用于輸入其他模塊提供的功能。
-
通過 export 命令定義了模塊的對外接口,其他 JS 文件就可以通過 import 命令加載這個模塊。
模塊的定義 /** * export 只支持對象形式導出,不支持值的導出,export default 命令用于指定模塊的默認輸出, * 只支持值導出,但是只能指定一個,本質(zhì)上它就是輸出一個叫做default 的變量或方法 */ // 寫法 1 export var m = 1; // 寫法 2 var m = 1; export { m }; // 寫法 3 var n = 1; export { n as m }; // 寫法 4 var n = 1; export default n; 模塊的引入 // 解構(gòu)引入 import { firstName, lastName, year } from 'a-module'; // 為輸入的變量重新命名 import { lastName as surname } from 'a-module'; // 引出模塊對象(引入所有) import * as ModuleA from 'a-module';
在使用 ES Module 值得注意的是:import 和 export 命令只能在模塊的頂層,在代碼塊中將會報錯
這是因為 ES Module 需要在編譯時期進行模塊靜態(tài)優(yōu)化,import 和 export 命令會被 JavaScript 引擎靜態(tài)分析,先于模塊內(nèi)的其他語句執(zhí)行,這種設(shè)計有利于編譯器提高效率,但也導致無法在運行時加載模塊(動態(tài)加載)。