*解析
模塊是可以實(shí)現(xiàn)特定功能的一組代碼。
由于JS不能handle超大規(guī)模代碼,所以要借鑒其它語言處理這種問題的方式:
把邏輯相關(guān)的代碼組織到同一個(gè)區(qū)域,區(qū)域內(nèi)部相對(duì)獨(dú)立和外界互不干擾;
外部使用時(shí)直接引用對(duì)應(yīng)的區(qū)域即可。
模擬類似的功能,用來隔離、組織復(fù)雜的JavaScript代碼,我們稱為模塊化。
*作用
1 解決命名沖突
2 解決互相依賴
3 提高代碼的可讀性
4 提高代碼的復(fù)用性
*規(guī)范
**CommonJS
解析:
服務(wù)器端使用的模塊規(guī)范 例如 Node.js
使用:
1 定義模塊 一個(gè)文件 = 一個(gè)模塊 = 一個(gè)單獨(dú)的作用域
2 模塊輸出: 唯一出口module.exports對(duì)象,存放輸出內(nèi)容
3 加載模塊: 使用require方法讀取文件并執(zhí)行,返回ta內(nèi)部的module.exports對(duì)象
舉例:
Model-1.js 1 定義模塊
var name = 'jrg';
function a(){...} name是模塊的內(nèi)部變量,只有文件內(nèi)的函數(shù)可以使用
function b(){...}
module.exports={ 2 模塊輸出 (導(dǎo)出接口)
a:a,
b:b
}
Model-2.js *新模塊 = 新文件
function c(){...}
module.exports = c
main.js 3 加載模塊
var xx = require('./Model-1');
var dd = require('./Model-2'); 使用哪個(gè)模塊再引入
xx.a()
dd.c() 然后就可以調(diào)用模塊提供的方法
**問題
Q CommonJS 規(guī)范在服務(wù)器端這么方便,那么瀏覽器端可以用嗎?
A NO!
R 服務(wù)器端的文件在本地 => 同步加載;瀏覽器端的文件在遠(yuǎn)程服務(wù)器 =>異步加載
····所以CommonJS模塊在瀏覽器環(huán)境中無法正常加載。
····瀏覽器端需要另外的規(guī)范來封裝模塊 => AMD & CMD
**AMD
解析:
瀏覽器端使用的模塊規(guī)范之一 Asynchronous Module Definition異步模塊定義
define函數(shù),定義模塊--------------------------------------------------
語法:
define(id?, dependencies?, factory)
參數(shù):
id:模塊名;如果沒有參數(shù),默認(rèn)為文件名。
dependencies:一個(gè)數(shù)組;當(dāng)前模塊 [依賴的模塊名稱集合]。
factory:工廠函數(shù);模塊初始化要執(zhí)行的函數(shù)(只執(zhí)行一次) or 對(duì)象(模塊的輸出值)。
require函數(shù),加載模塊-------------------------------------------------
語法: require([dependencies], function(){});
參數(shù): [dependencies]是一個(gè)數(shù)組,表示所依賴的模塊;
function(){} 是一個(gè)回調(diào)函數(shù),當(dāng)前面指定的模塊都加載成功后,以參數(shù)形式傳入該函數(shù),
然后函數(shù)內(nèi)部就可以使用這些模塊了。
舉例----------------------------------------------------------------------------
myModule1.js 定義模塊
define(['dependency'], function(){
function a(){...} // 使用面向?qū)ο蠓绞椒庋b好的組件
return a, // 留出接口
});
main.js 加載模塊
require(['myModule1','myModule2'], function (myModule1,myModule2){
myModule1.a(); // 調(diào)用組件
});
**CMD
解析:
國內(nèi)發(fā)展出來的模塊規(guī)范Common Module Definition通用模塊定義
define函數(shù),定義模塊---------------------------------------------------------
語法:
define(function(require, exports, module){ });
參數(shù):
require 是一個(gè)方法,獲取其他模塊的接口
exports 是一個(gè)對(duì)象,向外提供模塊接口
module 是一個(gè)對(duì)象,存儲(chǔ)了一些屬性和方法
舉例--------------------------------------------------------------------------
ModuleA.js 定義模塊
define(function(require, exports, module) { function的三個(gè)參數(shù)固定不變
var a = require('a.js') 函數(shù)內(nèi)部,用到誰就引入誰
a(); 寫法類似commonJS
ModuleA.exprots = xx;
});
main.js 加載模塊
seajs.use(['ModuleA.js'], function(ModuleA){ ... });
模塊化工具:
?由于不是JavaScript原生支持,
使用AMD需要用到對(duì)應(yīng)的庫函數(shù)RequireJS;
使用CMD需要用到對(duì)應(yīng)的庫函數(shù)SeaJS;
其實(shí)AMD和CMD分別 是RequireJS和SeaJS 在推廣過程中對(duì)模塊定義的規(guī)范化產(chǎn)出;
同樣倡導(dǎo)模塊化開發(fā)理念,核心價(jià)值是讓 JavaScript 的模塊化開發(fā)變得簡單自然。
?AMD和CMD的區(qū)別?
** 1** 關(guān)于依賴的模塊,加載都是異步,執(zhí)行處理不同
AMD:推崇依賴前置,提前執(zhí)行(依賴先執(zhí)行)
CMD:推崇依賴就近,延遲執(zhí)行(運(yùn)行到需加載,根據(jù)順序執(zhí)行)
** 2** 關(guān)于API
AMD:API根據(jù)使用范圍有區(qū)別,但使用同一個(gè)api接口
CMD:每個(gè)API的職責(zé)單一
**requireJS
?requireJS主要解決兩個(gè)問題:
1 實(shí)現(xiàn)js文件的異步加載,避免網(wǎng)頁失去響應(yīng);
2 管理模塊之間的依賴性,便于代碼的編寫和維護(hù)。
如果
不使用`requireJS`就要單獨(dú)加載每個(gè)js文件,像這樣:
<script src="1.js"></script>
<script src="2.js"></script>
<script src="3.js"></script>
缺點(diǎn)
1 加載的時(shí)候,瀏覽器會(huì)停止網(wǎng)頁渲染,加載文件越多,網(wǎng)頁失去響應(yīng)的時(shí)間就會(huì)越長;
2 由于js文件之間存在依賴關(guān)系,因此必須嚴(yán)格保證加載順序,依賴性最大的模塊必須放到最后,
當(dāng)依賴關(guān)系很復(fù)雜的時(shí)候,代碼的編寫和維護(hù)都會(huì)變得困難。
**總結(jié)
至此,已經(jīng)了解模塊規(guī)范,可以寫出符合規(guī)范的 模塊文件了!
以AMD為例,梳理一下使用過程:
1 下載require.js文件,放入根目錄
2 頁面引入requireJS庫<script src="http://require.js"></script> => 主文件
3 頁面引入主文件 <script src="http://main.js"></script> => 加載模塊
4 主文件 => 已放入根目錄的模塊文件
Tip:
主文件=>加載模塊=>require函數(shù)=>語法require([dependencies], function(){})
模塊文件=>定義模塊=>define函數(shù)=>語法define([dependencies], factory)
**r.js
這是一個(gè)打包工具
作用
把所有的模塊和框架js文件壓縮到同一個(gè)js文件里,
Before: 引入requireJS庫=>引入主文件=>查找模塊
After : 引入requireJS庫=>查找模塊
使用
1 下載r.js文件,放入根目錄
2 配置壓縮文件build.js
3 打開node,進(jìn)入目標(biāo)DEMO-AMD
4 執(zhí)行命令行node r.js -o build.js,會(huì)自動(dòng)生成壓縮文件
5 將頁面上的主文件地址main.js替換為壓縮后的文件index.merge.min.js
*應(yīng)用
▼DEMO-AMD
?index.html
?CSS
▼JS
▼lib 框架&庫目錄
?require.js
?r.js
?jquery.js
......
▼app 組件目錄
?gotop.js
......
?main.js 主文件
?build.js 壓縮配置文件
▼dist 輸出目錄
?index.merge.min.js
■ index.html
頁面加載 <script src="http://js/lib/require.js" data-main="js/main.js"></script>
□ src 先加載庫 => 執(zhí)行require.js => 解析后指向主文件
□ data-main 自定義屬性 => 執(zhí)行main.js => 主文件里是所有模塊的入口
■ main.js 主文件 =>
requirejs.config({ □ 配置路徑,方便查找
baseUrl: "./js/app", 默認(rèn)文件夾(加載時(shí)寫直接寫里面的模塊名)
paths: { 特殊文件/夾(相對(duì)于baseUrl)
'jquery': '../lib/jquery' 指向模塊 (加載時(shí)寫模塊名)
'lib':'../lib' 指向文件夾(加載時(shí)寫lib下模塊名)
}
});
requirejs(["GoTop","lib/xx",...],
function(GoTop,$,...) { □ 加載([依賴模塊],工廠函數(shù))
new GoTop(); 模塊加載完畢,作為參數(shù)傳入工廠函數(shù)
...... 執(zhí)行工廠函數(shù)
});
● 這里的baseUrl是main.js相對(duì)于index.html=> 從index出發(fā)找到app
● requirejs(["xxx"]) => 如果沒有工廠函數(shù),直接加載并執(zhí)行文件
■ build.js 壓縮配置文件 =>
({ □ 配置路徑
baseUrl: ".", 兩個(gè)baseUrl指向要保持一致(都指向app)
paths: {
'jquery': './lib/jquery',
'GoTop':'./com/GoTop',
......
},
name: "main", 配置入口
out: "./dist/index.merge.min.js" 配置出口(自定義)
});
● 這里的baseUrl是build.js相對(duì)于自身的,=> 從build 出發(fā)找到 app
但是!程序員永遠(yuǎn)有更便捷的方法 => webpack