前端的模塊化編程
對前端模塊化的認識
一個大型項目時候,通常需要一個團隊分工合作,模塊化編程就是開發者把邏輯相關的代碼放在一個module裏面,我們只需要實現核心的業務邏輯,
不用擔心命名沖突,需要時再加載就可以。
Javascript不是一種模塊化編程語言,開發者需要模擬出類似的功能,來將一個大程序拆分成相互依賴的小文件,我們稱為模塊化。
有了模塊,我們就可以更方便地使用別人的代碼,想要什麽功能,就加載什麽模塊。
早期模塊化編程的方法
1)函數
最早期,我們的函數的功能就是把實現特定邏輯的一組語句打包,在一個文件裏面編寫幾個函數就是最開始的模塊了
這樣在使用的時候直接調用函數就可以了,但是這種做法有一個很大的缺點,就是汙染了全局變量,很有可能早造成命名沖突,而且模塊成員之間也沒什麽關系
function fn1(){ statement } function fn2(){ statement }
2)對象
為了解決上面的問題,對象的寫法應運而生了,我們可以把所有的模塊成員封裝在一個對象當中。這樣我們在調用的時候只要引用對應的文件,然後調用對象的方法就可以,而且避免了全局變量汙染,只要保證模塊名唯一即可,而且模塊內的成員也有了關系,但是這個方法的缺點就是外部可以隨意修改內部成員
var mymodule{ var1:1, var2:2, fn1:function(){}, fn2:function(){} }
3)立即執行函數
我們可以使用立即執行函數來達到隱藏細節的目的,這樣在模塊外部無法修改我們內部的變量和函數
var mymodule=(function(){ var var1=1; var var2=2; function fn1(){}; function fn2(){}; return { fn1: fn1, fn2: fn2 }; })();
以上三種做法就是我們模塊化的基礎
模塊化規範
因為有了模塊,我們就可以更方便地使用別人的代碼,想要什麽功能,就加載什麽模塊。
這樣做的前提就是大家都以同樣的方式來編寫模塊,那麽有一個模塊化的規範就非常重要
在ES6之前,JavaScript模塊規範主要有兩種:CommonJS和AMD,前者用於服務器,後者用於瀏覽器
CommonJS
commonJS是用於服務器端的模塊化規範,node.js的模塊系統,就是參照CommonJS規範實現的
1)定義模塊
根據CommonJS規範,一個單獨的文件就是一個模塊,並且每個模塊都是單獨的作用域,在該模塊內定義的變量無法被其他模塊讀取,除非定義為global對象的屬性
2)模塊輸出
模塊只有一個出口,module.exports對象,我們把模塊需要輸出的內容放入該對象
3)加載模塊
使用require方法,該方法用於讀取一個模塊並執行,返回模塊內部的module.exports對象
//模塊定義 myModel.js var name = ‘Byron‘; function printName(){ console.log(name); } function printFullName(firstName){ console.log(firstName + name); } //模塊輸出 module.exports = { printName: printName, printFullName: printFullName } //加載模塊 var nameModule = require(‘./myModel.js‘); //調用模塊的方法 nameModule.printName();
AMD
AMD(異步模塊定義)是一個在瀏覽器端模塊化開發的規範,它采用異步方式加載模塊,模塊的加載不影響它後面語句的運行。也解決了模塊依賴性的問題。
requireJS定義了一個函數 define,它是全局變量,用來定義模塊
define(id?, dependencies?, factory);
- id:可選參數,用來定義模塊的標識
- dependencies:是一個當前模塊依賴的模塊名稱數組
- factory:工廠方法,模塊初始化要執行的函數或對象。如果為函數,它應該只被執行一次。如果是對象,此對象應該為模塊的輸出值
AMD也采用require()語句加載模塊,但是不同於CommonJS,它要求兩個參數:
require([module], callback);
- 第一個參數是一個數組,裏面的成員就是要加載的模塊
- 第二個參數callback,則是加載成功之後的回調函數。
所有依賴這個模塊的語句,都定義在一個回調函數中,等到加載完成之後,這個回調函數才會運行。解決了依賴性的問題。
// 定義模塊 myModule.js define([‘dependency‘], function(){ var name = ‘Byron‘; function printName(){ console.log(name); } return { printName: printName } }); // 加載模塊 require([‘myModule‘], function (my){ my.printName(); });
CMD
CMD(通用模塊定義)國內發展出來的,就像AMD有個requireJS,CMD有個SeaJS
eaJS要解決的問題和requireJS一樣,只不過在模塊定義方式和模塊加載時機上有所不同
MD推崇"依賴就近",所以一般不在define的參數中寫依賴,在factory中寫
define(id?, deps?, factory)
factory有三個參數
function(require, exports, module)
- require 是 factory 函數的第一個參數,是一個方法,接受 模塊標識 作為唯一參數,用來獲取其他模塊提供的接口
- exports 是一個對象,用來向外提供模塊接口
- module 是一個對象,上面存儲了與當前模塊相關聯的一些屬性和方法
// 定義模塊 myModule.js define(function(require, exports, module) { var $ = require(‘jquery.js‘) $(‘div‘).addClass(‘active‘); }); // 加載模塊 seajs.use([‘myModule.js‘], function(my){ });
AMD與CMD區別:
最明顯的區別就是在模塊定義時對依賴的處理不同
1)AMD推崇依賴前置,在定義模塊的時候就要聲明其依賴的模塊
2)CMD推崇就近依賴,只有在用到某個模塊的時候再去require
同樣都是異步加載模塊
1)AMD所有模塊都加載並且執行完後會進入require的回調函數,執行主邏輯,這樣的效果就是依賴模塊的執行順序和書寫順序不一定一致,看網絡速度,哪個先下載下來,哪個先執行,但是主邏輯一定在所有依賴加載完成後才執行
2)CMD加載完某個依賴模塊後並不執行,只是下載而已,在所有依賴模塊加載完成後進入主邏輯,遇到require語句的時候才執行對應的模塊,這樣模塊的執行順序和書寫順序是完全一致的
這也是很多人說AMD用戶體驗好,因為沒有延遲,依賴模塊提前執行了;CMD性能好,因為只有用戶需要的時候才執行的原因
前端的模塊化編程