- JS模塊化的基本原理
- commonjs規范
- commonjs在前端模塊化中的基本使用
- AMD與CMD規范剖析博客鏈接
一、JS模塊化基本原理
在JS沒有提出來模塊化的時候,開發JS項目比較簡單,同時也比較雜亂,第一個問題就是全局變量的管理,為了解決這個問題基本上都使用了命名空間和閉包兩個主流的解決方式,但是隨着硬件基礎和網絡環境的改善,前端開發也越來越復雜,需要協同合作,代碼復用的需求越來越多,復雜性帶來的第一個問題就是命名管理問題再次遇到挑戰,然后就是協同開發也被正式提上了議題,模塊化正式走入台前。
1 var a = 1; 2 //函數 3 function bindeEvent(){} 4 function addClass(){} 5 //命名空間 6 var obj1 = { 7 a:2, 8 foo:function(){} 9 } 10 var obj = { 11 a:2, 12 foo:function(){} 13 } 14 //閉包 15 (function($){ 16 var a = 123; 17 function foo(){} 18 return {foo:foo} 19 })(jQuery);
模塊化的在很大程度上與閉包的實現模式非常類似,傳入閉包的參數可以看作是模塊的引用,返回值可以看作是模塊定義的接口,整個閉包就可以理解為一個模塊,有差別的地方就是模塊化是一個模塊單獨在一個js文件中實現、模塊中使用的模塊自身以外的參數和屬性都必須依賴引入。
有了單獨js文件和各種規范的標准接口,就能很好的實現了協同開發這問題,解決了前端開發越來越麻煩的復雜性,同時命名沖突問題也在模塊的私有變量特性中迎刃而解。
二、commonjs規范
在commonjs官網的最上方有一句這樣的描述:javascript: not just for browsers any more!翻譯:javascript:不僅僅是針對瀏覽器了!最近兩年在前端很火的nodejs就是commonjs規范的實現,而webpack是基於nodejs實現的,自然而然就是commonjs規范的實現。雖然我們后期在js的開發中不一定使用commonjs規范,但是我們需要使用webpack作為前端工程化的實現工具,還有基於nodejs的模塊、插件、庫,所以對於我們理解和應用其他技術很有幫助,所以對於commonjs的理解是有必要的。
可能說到這里,我們還不清除commonjs是個什么東西,這么來說,javascript官方定義的api只能構建基礎的瀏覽器應用,也就是說javascript官方只實現了在瀏覽器環境中的api,如果來那javascript來做服務端應用程序,又或者是圖形界面應用程序,還有命令行工具等就有些力不從心,2009年程序員Ryan Dahl創造了node,js項目,將javascript用於服務器端編程,這標志這javascript模塊化開發的誕生,而commonjs就是這個模塊的規范。
commonjs都有哪些規范呢?
1.commonjs的出發點:js沒有模塊系統、標准庫較少、缺乏包管理工具;為了讓javascript可以在任何地方運行,以達到JAVA、C#、PHP這些后台語言的開發能力。
2.commonjs規范的具體內容:
一個文件就是一個模塊;
普通方式定義的變量、函數、對象都屬於該模塊內;
通過require來加載模塊;
通過exports和modul.exports來暴露模塊中的內容(接口);
3.所有代碼都運行在模塊作用域,不會污染全局作用域;模塊可以多次加載,但只會在第一次加載的時候運行,然后運行結果就被緩存了,以后再加載就讀取緩存里面的結果;模塊的加載順序按照代碼的出現順序同步加載。
4._dirname代表當前模塊文件所在的文件夾路徑,_filename代表當前模塊文件所在文件夾路徑+文件名。
5.require(同步加載基本功能):讀取並執行一個js文件,然后返回該模塊的exports對象,如果沒有發現指定模塊會報錯。
6.模塊內的exports:nodejs為每個模塊提供了一個exposrts變量,其指向module.exports,相當於再模塊頭部加了這句代碼:var exports = module.exports;在對外輸出時,可以給exports對象添加方法,但不能直接賦值,因為這樣會切斷exports與module.exports的聯系。
7.npm root -g 查看全局包安裝位置,建議nvm目錄下npm\node_modules目錄,然后設置npm全局包安裝位置npm config set prefix "",然后將該路徑添加到環境變量中;
8.npm init -y 初始化package.json文件,加上-y就會默認生成該文件;npm docs 包名:查看包的文檔;npm install:安裝package.json中dependencies屬性中所以來的包。
9.由於npm服務器是國外的,下載慢或者不能下載成功經常出現,建議使用淘寶NPM鏡像http://npm.taobao.org/,與官方NPM同步頻率為10分鍾一次,安裝命令:npm install -g cnpm --registry=https://registry.npm.taobao.org,安裝包:cnpm install 包名(其它命令基本一致);
10.如果你不想下載cnpm,npm還提供了一個鏡像源管理工具:npm install -g nrm,通過:nrm ls,查看鏡像源列表 ,通過:npm use 鏡像源,來切換;
11、NPM的模塊加載機制:
如果require的是絕對路徑文件,查找不會去遍歷每個node_modules目錄,其速度最快
1).從module.paths數組中(由當前執行文件目錄到磁盤根目錄)取出第一個目錄作為查找基准
2).直接從目錄中查找該文件,如果存在則結束查找,如果不存在則進行下一條查找
3).嘗試添加.js、.json、.node后綴之后查找,如果存在文件則結束查找,如果不存在則進行下一條查找
4).嘗試將require的參數作為一個包來進行查找,讀取目錄下的package.json文件,取得Main參數指定的文件
5).嘗試查找該文件,如果存在則結束查找,如果不存在則進行第3條查找
6).如果繼續失敗,則取出module.paths數組中的下一目錄作為基准查找,循環第1-5個步驟
7).如果繼續失敗,循環第1-6個步驟,直到module.paths中的最后一個值
8).如果繼續失敗,則拋出異常
三、commonjs在前端模塊化中的基本使用
Commonjs規范最開始就是為了將javascript運行在服務器環境下,所以采用了同步加載不同模塊文件,適用於服務端。因為模塊文件都存放在服務器的各個硬盤上,讀取速度塊,適合服務器,不適應瀏覽器。
而且瀏覽器不兼容Commonjs,原因是瀏覽器缺少module、exports、require、global是個環境變量。如果要使用的化需要工具轉換,有時候還會用到global這個nodejs中的全局變量,相當於dom中的window變量。
在前端開發中使用Commonjs模塊化規范:
1.定義文件結構:
//工作區間 -->modules//依賴模塊 -->m1.js -->m2.js -->m3.js -->demo.html//結構文本 -->index.js//主入口文件
依賴模塊代碼(m1.js):
1 module.exports = { 2 msg:'m1', 3 foo:function(){ 4 return this.msg; 5 } 6 }
依賴模塊代碼(m2.js):
1 module.exports = function(){ 2 return 'm2'; 3 }
依賴模塊代碼(m3.js):
1 // var exports = module.exports;在第博客二部分:commonjs規范中第六小點由說明 2 exports.foo = function(){ 3 return 'ms'; 4 }
主入口文件(index.js):
1 var m1 = require('./modules/m1'); 2 var m2 = require('./modules/m2'); 3 var m3 = require('./modules/m3'); 4 5 console.log(m1.foo()); 6 console.log(m2()); 7 console.log(m3.foo());
前面規范中說過在瀏覽器並不支持commonjs規范,主入口文件index.js肯定不能直接被demo.html結構文本使用,直接使用的話會報錯;所以在要前端開發中使用commonjs規范的話就必須使用插件將commonjs模塊化規范轉換成瀏覽器識別的代碼結構,在這之前系統上必須安裝nodejs環境,這時候我們可以先在控制台中測試看看index.js的依賴是否成功:
1 node -v //測試node環境是否安裝成功,如果成功的話會在控制台打印出node的版本號 2 node index.js
打印結果:
這是在node環境下能夠編譯執行index.js,前面說過要想將commonjs模塊化結構代碼在瀏覽器中編譯執行,得需要使用工具轉換成瀏覽器能夠編譯執行的代碼結構,這個工具就是Browserify,工具的官網:http://browserify.org/
下載安裝成功是這樣的:
在Browserify官網首頁的使用介紹中有這樣一句命令說明:
意思是將commonjs規范的main.js文件通過browserify轉換成瀏覽器能夠執行的bundle.js文件;那么我們就可以使用這個命令來將示例中的index.js轉換成一個可以在瀏覽器使用的js文件(下面這句命令是要在命令窗口執行):
browserify index.js -o bundle.js
這行命令執行完成后會在我們的工作區間生成一個bendle.js文件,這個文件就是通過browserify工具將基於commonjs規范的index.js入口文件及其依賴的m1.js、m2.js、m3.js轉換生成的瀏覽器可以執行的js代碼。
這時候我們可以將bundle.js引入到demo.html結構文件中進行執行:
<script src="bundle.js"></script>
打開頁面-查看瀏覽器控制台:
在前端使用commonjs規范然后使用browserify轉換成瀏覽器能執行的文件,全部過程演示完成;值得我們關注的是生成的bundle.js是什么?
從生成的代碼來看,本質上就是將依賴的文件合並到了一個文件中,然后同樣是采用閉包加對象屬性實現的命名空間的代碼管理方式,只不過這個過程我們交給了工具來實現,在我們的日常開發中,我們只需要關注我們自己的模塊開發,不再需要去考慮全局命名沖突,代碼管理問題。而且通過模塊化開發帶來的最大的好處就是代碼的復用性特別方便,采用依賴模式然后使用工具轉換,也解決了原來在結構文件中引入大量插件和庫的問題,給代碼瘦身的同時也降低了網絡請求壓力。特別是插件、庫、組件之間的相互依賴關系帶來的加載阻塞問題得到了很好的解決。
然后,在Browserify的官方首頁使用介紹中有一個基於全局模塊uniq的示例:
下載安裝uniq模塊:
npm install uniq
下載安裝成功以后會在當前工作區間生成一個node_modules文件夾和一個packge-lock.json文件,各相關的原理在webpack博客中詳細解析。然后,官網給出的代碼放到我們示例代碼中的index.js中:
1 var unique = require('uniq'); 2 var data = [1, 2, 2, 3, 4, 5, 5, 5, 6]; 3 console.log(unique(data));
然后,在控制台中再次編譯一次index.js到bundle.js:
browserify index.js -o bundle.js
然后刷新demo.html,查看瀏覽器控制台:
關於commonjs的實現原理等過段時間再來補充;
AMD、COM規范解析