前端模組化(CommonJs,AMD和CMD)
前端模組規範有三種:CommonJs,AMD和CMD。
CommonJs用在伺服器端,AMD和CMD用在瀏覽器環境。
AMD 是 RequireJS 在推廣過程中對模組定義的規範化產出。
CMD 是 SeaJS 在推廣過程中對模組定義的規範化產出。
AMD:提前執行(非同步載入:依賴先執行)+延遲執行
CMD:延遲執行(執行到需載入,根據順序執行)
模組
函式寫法
function f1(){ //... } function f2(){ //... }
模組就是實現特定功能的檔案,把幾個函式放在一個檔案裡就構成了一個模組。需要的時候載入這個檔案,呼叫其中的函式即可。
但這樣做會汙染全域性變數,無法保證不與其他模組發生變數名衝突,而且模組成員之間沒什麼關係。
物件寫法
var module = { star : 0, f1 : function (){ //... }, f2 : function (){ //... } }; module.f1(); module.star = 1;
模組寫成一個物件,模組成員都封裝在物件裡,通過呼叫物件屬性,訪問使用模組成員。但同時也暴露了模組成員,外部可以修改模組內部狀態。
立即執行函式
var module = (function(){ var star = 0; var f1 = function (){ console.log('ok'); }; var f2 = function (){ //... }; return { f1:f1, f2:f2 }; })(); module.f1(); //ok console.log(module.star) //undefined
外部無法訪問內部私有變數
CommonJs
CommonJS是伺服器端模組的規範,由Node推廣使用。由於服務端程式設計的複雜性,如果沒有模組很難與作業系統及其他應用程式互動。使用方法如下:
math.js exports.add = function() { var sum = 0, i = 0, args = arguments, l = args.length; while (i < l) { sum += args[i++]; } return sum; }; increment.js var add = require('math').add; exports.increment = function(val) { return add(val, 1); }; index.js var increment = require('increment').increment; var a = increment(1); //2
根據CommonJS規範:
- 一個單獨的檔案就是一個模組。每一個模組都是一個單獨的作用域,也就是說,在該模組內部定義的變數,無法被其他模組讀取,除非定義為global物件的屬性。
-
輸出模組變數的最好方法是使用module.exports物件。
-
載入模組使用require方法,該方法讀取一個檔案並執行,返回檔案內部的module.exports物件
仔細看上面的程式碼,您會注意到 require 是同步的。模組系統需要同步讀取模組檔案內容,並編譯執行以得到模組介面。
然而, 這在瀏覽器端問題多多。
瀏覽器端,載入 JavaScript 最佳、最容易的方式是在 document 中插入<script>標籤。但指令碼標籤天生非同步,傳統 CommonJS 模組在瀏覽器環境中無法正常載入。
解決思路之一是,開發一個伺服器端元件,對模組程式碼作靜態分析,將模組與它的依賴列表一起返回給瀏覽器端。 這很好使,但需要伺服器安裝額外的元件,並因此要調整一系列底層架構。
另一種解決思路是,用一套標準模板來封裝模組定義:
define(function(require, exports, module) { // The module code goes here });
這套模板程式碼為模組載入器提供了機會,使其能在模組程式碼執行之前,對模組程式碼進行靜態分析,並動態生成依賴列表。
math.js define(function(require, exports, module) { exports.add = function() { var sum = 0, i = 0, args = arguments, l = args.length; while (i < l) { sum += args[i++]; } return sum; }; }); increment.js define(function(require, exports, module) { var add = require('math').add; exports.increment = function(val) { return add(val, 1); }; }); index.js define(function(require, exports, module) { var inc = require('increment').increment; inc(1); // 2 });
AMD
AMD是"Asynchronous Module Definition"的縮寫,意思就是"非同步模組定義"。由於不是JavaScript原生支援,使用AMD規範進行頁面開發需要用到對應的庫函式,也就是大名鼎鼎RequireJS,實際上AMD 是 RequireJS 在推廣過程中對模組定義的規範化的產出
它採用非同步方式載入模組,模組的載入不影響它後面語句的執行。所有依賴這個模組的語句,都定義在一個回撥函式中,等到載入完成之後,這個回撥函式才會執行。
RequireJS主要解決兩個問題
- 多個js檔案可能有依賴關係,被依賴的檔案需要早於依賴它的檔案載入到瀏覽器
- js載入的時候瀏覽器會停止頁面渲染,載入檔案越多,頁面失去響應時間越長
RequireJs也採用require()語句載入模組,但是不同於CommonJS,它要求兩個引數:
第一個引數[module],是一個陣列,裡面的成員就是要載入的模組;第二個引數callback,則是載入成功之後的回撥函式。math.add()與math模組載入不是同步的,瀏覽器不會發生假死。
require([module], callback); require([increment'], function (increment) { increment.add(1); });
define()函式
RequireJS定義了一個函式 define,它是全域性變數,用來定義模組:
define(id?, dependencies?, factory);
引數說明:
-
id:指定義中模組的名字,可選;如果沒有提供該引數,模組的名字應該預設為模組載入器請求的指定指令碼的名字。如果提供了該引數,模組名必須是“頂級”的和絕對的(不允許相對名字)。
-
依賴dependencies:是一個當前模組依賴的,已被模組定義的模組標識的陣列字面量。
依賴引數是可選的,如果忽略此引數,它應該預設為["require", "exports", "module"]。然而,如果工廠方法的長度屬性小於3,載入器會選擇以函式的長度屬性指定的引數個數呼叫工廠方法。 -
工廠方法factory,模組初始化要執行的函式或物件。如果為函式,它應該只被執行一次。如果是物件,此物件應該為模組的輸出值。
來舉個例子看看:
define("alpha", ["require", "exports", "beta"], function (require, exports, beta) { exports.verb = function() { return beta.verb(); //Or: return require("beta").verb(); } });
RequireJs使用例子
require.config是用來定義別名的,在paths屬性下配置別名。然後通過requirejs(引數一,引數二);引數一是陣列,傳入我們需要引用的模組名,第二個引數是個回撥函式,回撥函式傳入一個變數,代替剛才所引入的模組。
main.js //別名配置 requirejs.config({ paths: { jquery: 'jquery.min' //可以省略.js } }); //引入模組,用變數$表示jquery模組 requirejs(['jquery'], function ($) { $('body').css('background-color','red'); });
引入模組也可以只寫require()。requirejs通過define()定義模組,定義的引數上同。在此模組內的方法和變數外部是無法訪問的,只有通過return返回才行.
math.js define('math',['jquery'], function ($) {//引入jQuery模組 return { add: function(x,y){ return x + y; } }; });
將該模組命名為math.js儲存。
require(['jquery','math'], function ($,math) { console.log(math.add(10,100));//110 });
main.js引入模組方法
CMD
CMD 即Common Module Definition通用模組定義,CMD規範是國內發展出來的,就像AMD有個requireJS,CMD有個瀏覽器的實現SeaJS,SeaJS要解決的問題和requireJS一樣,只不過在模組定義方式和模組載入(可以說執行、解析)時機上有所不同。
在 CMD 規範中,一個模組就是一個檔案。程式碼的書寫格式如下:
define(function(require, exports, module) { // 模組程式碼 });
require是可以把其他模組匯入進來的一個引數;而exports是可以把模組內的一些屬性和方法匯出的;module 是一個物件,上面儲存了與當前模組相關聯的一些屬性和方法。
AMD是依賴關係前置,在定義模組的時候就要宣告其依賴的模組;
CMD是按需載入依賴就近,只有在用到某個模組的時候再去require:
// CMD define(function(require, exports, module) { var a = require('./a') a.doSomething() // 此處略去 100 行 var b = require('./b') // 依賴可以就近書寫 b.doSomething() // ... }) // AMD 預設推薦的是 define(['./a', './b'], function(a, b) { // 依賴必須一開始就寫好 a.doSomething() // 此處略去 100 行 b.doSomething() ... })
seajs使用例子
// 定義模組 myModule.js define(function(require, exports, module) { var $ = require('jquery.js') $('div').addClass('active'); exports.data = 1; }); // 載入模組 seajs.use(['myModule.js'], function(my){ var star= my.data; console.log(star); //1 });
參考
這篇《前端模組化:CommonJs,AMD和CDM》主要是個人對以下文章的總結,感謝這些老司機們的分享。
相關文章
- 前端模組化:CommonJS,AMD,CMD,ES6前端JS
- 前端模組化AMD、CMD、CommonJS&ES6前端JS
- 模組化之AMD、CMD、UMD、commonJSJS
- 前端模組化:CommonJS,AMD,CMD,ES6(轉載)前端JS
- 關於前端模組化 CommonJS、AMD、CMD、ES6中模組載入前端JS
- js模組 - amd cmd commonjs esm umdJS
- 前端模組化,AMD和CMD的區別總結前端
- web系列之模組化——AMD、CMD、CommonJS、ES6 整理&&比較WebJS
- 前端模組化之CommonJS前端JS
- 前端模組化之AMD與CMD原理(附原始碼)前端原始碼
- AMD , CMD, CommonJS,ES Module,UMDJS
- CommonJS,AMD,CMD,ES6,require 和 import 詳解JSUIImport
- CommonJS模組 和 ECMAScript模組JS
- 前端模組化IIFE,commonjs,AMD,UMD,ES6 Module規範超詳細講解前端JS
- CommonJS、AMD、CMD、ES Module 一文流JS
- 前端進階課程之模組化(一)CommonJS規範前端JS
- Commonjs規範與模組化JS
- JS模組化—CJS&AMD&CMD&ES6-前端面試知識點查漏補缺JS前端面試
- ES6 模組化與 CommonJS 模組化區別JS
- [面試專題]JS中模組AMD,CMD,import面試JSImport
- 03_Node js 模組化 CommonJSJS
- 前端進階課程之模組化(三)CMD規範前端
- 前端進階課程之模組化(二)AMD規範前端
- ES Module,commonjs和Typescript模組系統JSTypeScript
- 前端模組化前端
- 你真的懂模組化嗎?教你CommonJS實現JS
- 多種模組格式,包括 ES, CommonJS, UMD, AMD, SystemJS 和 IIFE的區別點分別是什麼JS
- 一覽js模組化:從CommonJS到ES6JS
- ES6模組與commonJS模組的差異JS
- 分而治之-前端模組化前端
- 讀懂CommonJS的模組載入JS
- 基於CommonJS規範,簡單實現NodeJs模組化NodeJS
- js中AMD和CMD的區別JS
- 前端模組化規範前端
- 淺談前端模組化前端
- 前端模組化基礎前端
- 前端模組化的前世前端
- 前端模組化雜記前端
- 前端模組化彙總前端