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