當項目越來越大時,會遇到一些問題:
1.命名沖突
2.文件依賴
所有就有了javascript模塊化開發概念。
模塊化開發的演變:
1.函數塊:最開始用全局函數將代碼塊包括在函數體內,然后把很多函數寫在一個js文件,然后引入js文件,這種方式會導致:全局變量污染和命令沖突,模塊之間的關系也不明確。
2.命名空間:把函數和變量封裝在對象里,可以較好的避免命名沖突問題。但是這方式會導致:多層級嵌套(命名空間越來越長),暴露了所有的模塊成員,內部狀態可以被外部改寫,不安全。
3.私有共有成員分離:將函數包裹在立即執行函數(達到函數外部無法訪問內部變量的效果)里,以返回的方式選擇性對外暴露內部成員。在立即執行函數里形成了一個私有作用域,私有空間的變量和函數不會影響到全局作用域。從某種意義上來說,解決了變量命名沖突的問題。
var calculator = (function () { // 這里形成一個單獨的私有的空間 // 私有成員的作用: // 1、將一個成員私有化 // 2、抽象公共方法(其他成員中會用到的) // 私有的轉換邏輯 function convert(input){ return parseInt(input); } function add(a, b) { return convert(a) + convert(b); } function subtract(a, b) {} function multiply(a, b) {} function divide(a, b) {} return { add : add, subtract : subtract, multiply : multiply, divide : divide } })();
4.模塊的拓展與維護
// 計算模塊 (function (calculator) { function convert(input) { return parseInt(input); } calculator.add = function(a, b) { return convert(a) + convert(b); } window.calculator = calculator; })(window.calculator || {}); // 新增需求 (function (calculator) { calculator.remain = function (a , b) { return a % b; } window.calculator = calculator; })(window.calculator || {}); alert(calculator.remain(4,3));
這種方式有利於對龐大的模塊的子模塊划分。
5.第三方依賴的管理
(function (calculator , $) { // 依賴函數的參數,是屬於模塊內部 // console.log($); calculator.remain = function (a , b) { return a % b; } window.calculator = calculator; })(window.calculator || {} , jQuery);
傳入依賴模塊jQuery, 立即執行函數空間里就可以使用第三方依賴了。
創建模塊的原則:高內聚低耦合,模塊內相關性高,模塊間關聯低。單一職責
模塊使用場景:
1.業務復雜
2.重復邏輯多
3.可拓展性要求高
最后演變到模塊化規范:
服務端主要有CommonJS(Node.js 就是基於CommonJS規范實現的模塊化 ,webpack也實現了對CommonJS原生支持)
CommonJS的核心思想就是通過 require 方法來同步加載所要依賴的其他模塊,然后通過 exports 或者 module.exports 來導出需要暴露的接口,這個規范里,每個文件就是一個模塊
其內部定義的變量是屬於這個模塊的,不會對外暴露,也就是說不會污染全局變量。例如
// a.js var x = 5; var addX = function (value) { return value + x; }; module.exports.x = x; module.exports.addX = addX;
這里的module代表了這個模塊,module的exports屬性就是對外暴露提供的接口,可以對外導出外部可以訪問的變量,如x, addX
這樣就可以在其他模塊中引入這個模塊了
var example = require('./a.js'); console.log(example.x); // 5
CommonJS規范是同步加載模塊的,在服務器端,文件都是保存在硬盤上,所以同步加載沒有問題,但是對於瀏覽器端,需要將文件從服務器端請求過來,那么同步加載就不適用了,所以,CommonJS是不適用於瀏覽器端的。
客戶端主要有:AMD( 實現有RequireJS ) / CMD(實現有SeaJS),隨着ES6模塊化規范的實現和普及,第三方的模塊化實現將會慢慢的淘汰。
AMD是非同步加載模塊,允許函數回調,如Require.js,需提前加載所有模塊。
CMD是按需加載模塊的(可以依賴就近),不需要在模塊開始就加載所有依賴。
AMD和CMD 規范 模塊加載區別:前者是對於依賴的模塊提前執行,后者是延遲執行。前者提倡依賴前置,后者提倡依賴就近,只需要在用到某個模塊時再require.
// AMD define(['./a', './b'], function(a, b) { // 依賴必須一開始就寫好 注意標紅的,通過函數回調的方式將引入的依賴賦值給標紅的變量,然后整個塊就可以利用這個引入的依賴了 a.doSomething() // 此處略去 100 行 b.doSomething() ... }); // CMD define(function(require, exports, module) { var a = require('./a') a.doSomething() // 此處略去 100 行 var b = require('./b') // 依賴可以就近書寫 b.doSomething() // ... });
JavaScript 模塊化主要有三種方案:
1.CommonJS
// module math.js module.exports = function add (a, b) { return a + b; } // main.js var {add} = require('./math'); console.log('1 + 2 = ' + add(1,2);
2.AMD / CMD
// module add.js define(function () { return { add: function (a, b) add { return a + b; } }; }); // main.js require(['add'], function (add) { console.log('1 + 2 = ' + add(1,2); });
3.ES6
// module add.js export function add (a, b) { return a + b; } // main.js import {add} from 'add.js';
之前的幾種模塊化方案都是前端社區自己實現的,只是得到了大家的認可和廣泛使用,而ES6的模塊化方案是真正的規范。 在ES6中,我們可以使用 import
關鍵字引入
模塊,通過 export
關鍵字導出
模塊,功能較之於前幾個方案更為強大,也是我們所推崇的,但是由於ES6目前無法在瀏覽器中執行,所以,我們只能通過babel
將不被支持的import編譯為當前受到廣泛支持的 require。
之前在想這個問題之前,一直有個疑問,瀏覽器怎么支持的require,exports, module, define 這些字段的? 。。。
很二逼的疑問,因為 require.js sea.js 就是用來支持瀏覽器提供這些字段的,也就是說在我們自己編寫的模塊之前需要先引入這些規范實現的庫文件
<script src="./require.js"></script>
<script src="./otherModule.js"></script>
參考鏈接:https://www.jianshu.com/p/3832c00a44a7
http://www.ruanyifeng.com/blog/2015/05/commonjs-in-browser.html