本文采取循序渐进的方式,从理论到实践,从RequireJS官方API文档中,总结出在使用RequireJS过程中最常用的一些用法,并对文档中不够清晰具体的内容,加以例证和分析,分享给大家供大家参考,具体内容如下
1. 模块化
相信每个前端开发人员在刚开始接触js编程时,都写过类似下面这样风格的代码:
<script type="text/javascript"> var a = 1; var b = 2; var c = a * a + b * b; if(c> 1) { alert('c > 1'); } function add(a, b) { return a + b; } c = add(a,b); </script>
<a href="javascript:;" title="">请点击</a>
这些代码的特点是:
到处可见的全局变量
大量的函数
内嵌在html元素上的各种js调用
当然这些代码本身在实现功能上并没有错误,但是从代码的可重用性,健壮性以及可维护性来说,这种编程方式是有问题的,尤其是在页面逻辑较为复杂的应用中,这些问题会暴露地特别明显:
全局变量极易造成命名冲突
函数式编程非常不利于代码的组织和管理
内嵌的js调用很不利于代码的维护,因为html代码有的时候是十分臃肿和庞大的
所以当这些问题出现的时候,js大牛们就开始寻找去解决这些问题的究极办法,于是模块化开发就出现了。正如模块化这个概念的表面意思一样,它要求在编写代码的时候,按层次,按功能,将独立的逻辑,封装成可重用的模块,对外提供直接明了的调用接口,内部实现细节完全私有,并且模块之间的内部实现在执行期间互不干扰,最终的结果就是可以解决前面举例提到的问题。一个简单遵循模块化开发要求编写的例子:
//module.js var student = function (name) { return name && { getName: function () { return name; } }; }, course = function (name) { return name && { getName: function () { return name; } } }, controller = function () { var data = {}; return { add: function (stu, cour) { var stuName = stu && stu.getName(), courName = cour && cour.getName(), current, _filter = function (e) { return e === courName; }; if (!stuName || !courName) return; current = data[stuName] = data[stuName] || []; if (current.filter(_filter).length === 0) { current.push(courName); } }, list: function (stu) { var stuName = stu && stu.getName(), current = data[stuName]; current && console.log(current.join(';')); } } }; //main.js var stu = new student('lyzg'), c = new controller(); c.add(stu,new course('javascript')); c.add(stu,new course('html')); c.add(stu,new course('css')); c.list(stu);
以上代码定义了三个模块分别表示学生,课程和控制器,然后在main.js中调用了controller提供的add和list接口,为lyzg这个学生添加了三门课程,然后在控制台显示了出来。运行结果如下:
javascript;html;css
通过上例,可以看出模块化的代码结构和逻辑十分清晰,代码看起来十分优雅,另外由于逻辑都通过模块拆分,所以达到了解耦的目的,代码的功能也会比较健壮。不过上例使用的这种模块化开发方式也并不是没有问题,这个问题就是它还是把模块引用如student这些直接添加到了全局空间下,虽然通过模块减少了很多全局空间的变量和函数,但是模块引用本身还是要依赖全局空间,才能被调用,当模块较多,或者有引入第三方模块库时,仍然可能造成命名冲突的问题,所以这种全局空间下的模块化开发的方式并不是最完美的方式。目前常见的模块化开发方式,全局空间方式是最基本的一种,另外常见的还有遵循AMD规范的开发方式,遵循CMD规范的开发方式,和ECMAScript 6的开发方式。需要说明的是,CMD和ES6跟本文的核心没有关系,所以不会在此介绍,后面的内容主要介绍AMD以及实现了AMD规范的RequireJS。
2. AMD规范
正如上文提到,实现模块化开发的方式,另外常见的一种就是遵循AMD规范的实现方式,不过AMD规范并不是具体的实现方式,而仅仅是模块化开发的一种解决方案,你可以把它理解成模块化开发的一些接口声明,如果你要实现一个遵循该规范的模块化开发工具,就必须实现它预先定义的API。比如它要求在加载模块时,必须使用如下的API调用方式:
require([module], callback) 其中: [module]:是一个数组,里面的成员就是要加载的模块; callback:是模块加载完成之后的回调函数