NodeJS的一套比較簡潔 Moudles 規範, 使得在服務器端的模塊化變得更加簡單。很長一段時間,不少公司或者項目都有本身的一套模塊化機制, 卻未能造成一套統一的標準, NodeJS的Moudles規範若是運用在瀏覽器端會存在一些問題,如javascript
採用XHR的方式實現同步請求模塊,存在明顯的跨域缺陷,而使用script的方式,默認是異步的。 在這樣的背景下, CommonJS的Modules/Wrappings、AMD、CMD等規範應時而生。html
Modules/Wrappings 規範的約定以下,更多請參考:http://wiki.commonjs.org/wiki/Modules/Wrappings前端
A basic wrapped module:java
module.declare(function(require, exports, module){ exports.foo = "bar"; });
AMD:全稱爲異步模塊定義, 是專門爲瀏覽器中JavaScript環境設計的規範. 規範自己很是簡單, 歸納以下:jquery
define(id?, dependencies?, factory);git
id: 模塊名github
dependencies: 依賴的模塊,若是缺省,默認爲 ["require", "exports", "module"]正則表達式
factory: 模塊工廠,經過排列組合這種模塊定義能知足不少場景的需求api
AMD在定義本身的Module規範的同時,也簡單兼容了CommonJS的Modules/Wrappings。跨域
define(["beta"], function (beta) { exports.verb = function() { return beta.verb(); } }); define('alpha', ["beta"], function (beta) { exports.verb = function() { return beta.verb(); } });
匿名模塊也帶來一些好處,如減小維護成本,使得模塊的源代碼與它的標識分離。從而實如今不改變模塊代碼的狀況下移動源碼文件的位置等。
define(function(require, exports, module){ var query = require("query"); var on = require("on"); ... });
AMD模塊加載器將會掃描該工廠函數的require調用,並自動的在運行該工廠方法以前加載他們, 也是和CMD規範的重要區別, 不少人在討論到AMD、CMD上都會探討這個問題,有許多爭議, 不過這點也正是體現了AMD規範的核心:模塊依賴必須在真正執行具體的factory方法前解決。
RequireJS是目前最流行的AMD加載器,從代碼中能夠看出,爲了支持CommonJS Wrapping, define會解析工廠函數的FunctionBody,去掉註釋,並將require的模塊匹配出來, 加到dependencies數組中。
var commentRegExp = /(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg, cjsRequireRegExp = /[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g; if (!deps && isFunction(callback)) { deps = []; if (callback.length) { callback .toString() .replace(commentRegExp, '') .replace(cjsRequireRegExp, function (match, dep) { deps.push(dep); }); deps = (callback.length === 1 ? ['require'] : ['require', 'exports', 'module']).concat(deps); } }
因爲實際項目中的FunctionBody狀況比較複雜,cjsRequireRegExp,這個匹配規則不是很強大,若是在實際項目中發現require有問題的地方, 能夠將FunctionBody手動匹配一下這個正則表達式, 看是否有問題。
exports有什麼好處?
一、相對使用return輸出對象, 因爲js語言特性決定,代碼依次執行, 當模板存在循環依賴關係時,這種export導出就會特別有用。
二、相對於命名空間導出API,它的好處在於它不會影響全局空間, 並且導出的API所屬的對象名稱不會和模塊代碼強耦合。 這也是YUI的模塊API導出方式採用命名空間的一點侷限。
對於一些僅僅提供數據或者獨立方法的模塊,factory格式變爲obj就能夠了, 如:
define({ camelCase: function(x) { return string.camelCase('abc ABC'); } });
一個插件依賴應該被描述爲以下格式:
[Plugin Module ID]![resource ID]
目前流行的RequireJS、curl、Dojo等支持AMD的加載器都支持插件, 如加載各類格式的數據,在domReady 完成後在執行操做等。因爲「!」被插件語法佔用,因此在通常資源請求中,請不要使用帶有「!」的URL。 完整的插件清單能夠參考http://requirejs.org/docs/download.html#plugins
下面是兩個RequireJS使用插入的例子:
define([ 'backbone', 'text!templates.html' ], function( Backbone, template ){ // ... }); require(['domReady!'], function (doc) { //This function is called once the DOM is ready, //notice the value for 'domReady!' is the current //document. });
jquery是如何作的?
if ( typeof define === "function" && define.amd && define.amd.jQuery ) { define("jquery", [], function () { return jQuery; } ); }
須要在加載中設置:
define.amd = { jQuery: true };
AMD僅僅提供了模塊定義的規則,在實際項目使用中還須要考慮一些模塊合併、打包方案等。
目前,實現AMD規範的庫有RequireJS 、curl 、Dojo 、bdLoad 、JSLocalnet 、Nodules等,也有不少庫支持AMD規範,即將本身做爲一個模塊存在,如MooTools 、jQuery 、qwery 、bonzo、firebug等。 在前端模塊化發展如此快的今天,將來應該會有不少庫或者模塊會支持做爲知足AMD、CMD or CommonJS Module規範的模塊存在。
隨着模塊化的發展,高質量的模塊會愈來愈多,爲了讓你們有統一的規則來使用模塊, 模塊規範化就顯得格外重要。 而無論是哪種模塊規範,將來在瀏覽器端前端是否也能夠像Nodejs同樣靈活的去使用知足相同規範,甚至各類不一樣規範的優秀模塊, 這樣就不用將代碼侷限同一種框架中, 或許只是本身瞎想。
參考:
http://wiki.commonjs.org/wiki/Modules/1.1.1#Module_Identifiers
https://github.com/amdjs/amdjs-api