個人github(PS:但願star): https://github.com/tonyzheng1...javascript
今天因爲項目中引入的echarts的文件太大,requirejs常常加載超時,不得不分開來加載echarts的各個圖表。可是使用echarts自帶的在線構建工具生成的支持AMD 標準的模塊報錯,因此不得不使用echarts的全局函數,使用requirejs的shim進行加載。藉此機會學習一下AMD, CMD, CommonJS和UMD各自的規範,和它們之間的區別。html
在瞭解這些規範以前,仍是先了解一下什麼是模塊化。前端
模塊化是指在解決某一個複雜問題或者一系列的雜糅問題時,依照一種分類的思惟把問題進行系統性的分解以之處理。模塊化是一種處理複雜系統分解爲代碼結構更合理,可維護性更高的可管理的模塊的方式。能夠想象一個巨大的系統代碼,被整合優化分割成邏輯性很強的模塊時,對於軟件是一種何等意義的存在。對於軟件行業來講:解耦軟件系統的複雜性,使得無論多麼大的系統,也能夠將管理,開發,維護變得「有理可循」。java
還有一些對於模塊化一些專業的定義爲:模塊化是軟件系統的屬性,這個系統被分解爲一組高內聚,低耦合的模塊。那麼在理想狀態下咱們只須要完成本身部分的核心業務邏輯代碼,其餘方面的依賴能夠經過直接加載被人已經寫好模塊進行使用便可。git
首先,既然是模塊化設計,那麼做爲一個模塊化系統所必須的能力:github
定義封裝的模塊。編程
定義新模塊對其餘模塊的依賴。api
可對其餘模塊的引入支持。數組
好了,思想有了,那麼總要有點什麼來創建一個模塊化的規範制度吧,否則各式各樣的模塊加載方式只會將局攪得更爲混亂。那麼在JavaScript中出現了一些非傳統模塊開發方式的規範 CommonJS的模塊規範,AMD(Asynchronous Module Definition),CMD(Common Module Definition)等。瀏覽器
CommonJS是服務器端模塊的規範,Node.js採用了這個規範。
根據CommonJS規範,一個單獨的文件就是一個模塊。加載模塊使用require方法,該方法讀取一個文件並執行,最後返回文件內部的exports對象。
例如:
// foobar.js //私有變量 var test = 123; //公有方法 function foobar () { this.foo = function () { // do someing ... } this.bar = function () { //do someing ... } } //exports對象上的方法和變量是公有的 var foobar = new foobar(); exports.foobar = foobar;
//require方法默認讀取js文件,因此能夠省略js後綴 var test = require('./boobar').foobar; test.bar();
CommonJS 加載模塊是同步的,因此只有加載完成才能執行後面的操做。像Node.js主要用於服務器的編程,加載的模塊文件通常都已經存在本地硬盤,因此加載起來比較快,不用考慮異步加載的方式,因此CommonJS規範比較適用。但若是是瀏覽器環境,要從服務器加載模塊,這是就必須採用異步模式。因此就有了 AMD CMD 解決方案。
AMD是"Asynchronous Module Definition"的縮寫,意思就是"異步模塊定義".
AMD設計出一個簡潔的寫模塊API:
define(id?, dependencies?, factory);
第一個參數 id 爲字符串類型,表示了模塊標識,爲可選參數。若不存在則模塊標識應該默認定義爲在加載器中被請求腳本的標識。若是存在,那麼模塊標識必須爲頂層的或者一個絕對的標識。
第二個參數,dependencies ,是一個當前模塊依賴的,已被模塊定義的模塊標識的數組字面量。
第三個參數,factory,是一個須要進行實例化的函數或者一個對象。
經過參數的排列組合,這個簡單的API能夠從容應對各類各樣的應用場景,以下所述。
定義無依賴的模塊
define( { add : function( x, y ){ return x + y ; } } );
定義有依賴的模塊
define(["alpha"], function( alpha ){ return { verb : function(){ return alpha.verb() + 1 ; } } });
定義數據對象模塊
define({ users: [], members: [] });
具名模塊
define("alpha", [ "require", "exports", "beta" ], function( require, exports, beta ){ export.verb = function(){ return beta.verb(); // or: return require("beta").verb(); } });
包裝模塊
define(function(require, exports, module) { var a = require('a'), b = require('b'); exports.action = function() {}; } );
不考慮多了一層函數外,格式和Node.js是同樣的:使用require獲取依賴模塊,使用exports導出API。
除了define外,AMD還保留一個關鍵字require。require 做爲規範保留的全局標識符,能夠實現爲 module loader,也能夠不實現。
require([module], callback)
AMD模塊化規範中使用全局或局部的require函數實現加載一個或多個模塊,全部模塊加載完成以後的回調函數。
其中:
[module]:是一個數組,裏面的成員就是要加載的模塊;
callback:是模塊加載完成以後的回調函數。
例如:加載一個math模塊,而後調用方法 math.add(2, 3);
require(['math'], function(math) { math.add(2, 3); });
RequireJS 是一個前端的模塊化管理的工具庫,遵循AMD規範,它的做者就是AMD規範的創始人 James Burke。因此說RequireJS是對AMD規範的闡述一點也不爲過。
RequireJS 的基本思想爲:經過一個函數來將全部所須要的或者說所依賴的模塊實現裝載進來,而後返回一個新的函數(模塊),咱們全部的關於新模塊的業務代碼都在這個函數內部操做,其內部也可無限制的使用已經加載進來的以來的模塊。
<script data-main='scripts/main' src='scripts/require.js'></script>
那麼scripts下的main.js則是指定的主代碼腳本文件,全部的依賴模塊代碼文件都將從該文件開始異步加載進入執行。
define用於定義模塊,RequireJS要求每一個模塊均放在獨立的文件之中。按照是否有依賴其餘模塊的狀況分爲獨立模塊和非獨立模塊。
獨立模塊,不依賴其餘模塊。直接定義:
define({ method1: function(){}, method2: function(){} });
也等價於
define(function() { return { method1: function(){}, method2: function(){} } });
非獨立模塊,對其餘模塊有依賴。
define([ 'module1', 'module2' ], function(m1, m2) { ... });
或者:
define(function(require) { var m1 = require('module1'), m2 = require('module2'); ... });
簡單看了一下RequireJS的實現方式,其 require 實現只不過是提取 require 以後的模塊名,將其放入依賴關係之中。
require方法調用模塊
在require進行調用模塊時,其參數與define相似。
require(['foo', 'bar'], function(foo, bar) { foo.func(); bar.func(); } );
在加載 foo 與 bar 兩個模塊以後執行回調函數實現具體過程。
固然還能夠如以前的例子中的,在define定義模塊內部進行require調用模塊
define(function(require) { var m1 = require( 'module1' ), m2 = require( 'module2' ); ... });
define 和 require 這兩個定義模塊,調用模塊的方法合稱爲AMD模式,定義模塊清晰,不會污染全局變量,清楚的顯示依賴關係。AMD模式能夠用於瀏覽器環境而且容許非同步加載模塊,也能夠按需動態加載模塊。
官網 (http://www.requirejs.org/)
API (http://www.requirejs.org/docs...
CMD是SeaJS 在推廣過程當中對模塊定義的規範化產出
對於依賴的模塊AMD是提早執行,CMD是延遲執行。不過RequireJS從2.0開始,也改爲能夠延遲執行(根據寫法不一樣,處理方式不經過)。
CMD推崇依賴就近,AMD推崇依賴前置。
//AMD define(['./a','./b'], function (a, b) { //依賴一開始就寫好 a.test(); b.test(); }); //CMD define(function (requie, exports, module) { //依賴能夠就近書寫 var a = require('./a'); a.test(); ... //軟依賴 if (status) { var b = requie('./b'); b.test(); } });
雖然 AMD也支持CMD寫法,但依賴前置是官方文檔的默認模塊定義寫法。
AMD的API默認是一個當多個用,CMD嚴格的區分推崇職責單一。例如:AMD裏require分全局的和局部的。CMD裏面沒有全局的 require,提供 seajs.use()來實現模塊系統的加載啓動。CMD裏每一個API都簡單純粹。
UMD是AMD和CommonJS的糅合
AMD模塊以瀏覽器第一的原則發展,異步加載模塊。
CommonJS模塊以服務器第一原則發展,選擇同步加載,它的模塊無需包裝(unwrapped modules)。
這迫令人們又想出另外一個更通用的模式UMD (Universal Module Definition)。但願解決跨平臺的解決方案。
UMD先判斷是否支持Node.js的模塊(exports)是否存在,存在則使用Node.js模塊模式。
在判斷是否支持AMD(define是否存在),存在則使用AMD方式加載模塊。
(function (window, factory) { if (typeof exports === 'object') { module.exports = factory(); } else if (typeof define === 'function' && define.amd) { define(factory); } else { window.eventUtil = factory(); } })(this, function () { //module ... });