在早期编写JavaScript时,咱们只需在<script>标签内写入JavaScript的代码就能够知足咱们对页面交互的须要了。但随着时间的推移,时代的发展,本来的那种简单粗暴的编写方式所带来的诸如逻辑混乱,页面复杂,可维护性差,全局变量暴露等问题接踵而至,前辈们为了解决这些问题提出了很种的解决方案,其中之一就是JavaScript模块化编程。总的来讲,它有如下四种优势:css
2009年Node.js横空出世,将JavaScript带到了服务器端领域。而对于服务器端来讲,没有模块化那但是不行的。所以CommonJs社区的大牛们开始发力了,制定了一个与社区同名的关于模块化的规范——CommonJs。它的规范主要以下:前端
根据CommonJS规范的规定,每一个文件就是一个模块,有本身的做用域,也就是在一个文件里面定义的变量、函数、类,都是私有的,对其余文件是不可见的。通俗来说,就是说在模块内定义的变量和函数是没法被其余的模块所读取的,除非定义为全局对象的属性。jquery
1 // addA.js 2 const a = 1; 3 const addA = function(value) { 4 return value + a; 5 }
上面代码中,变量a和函数addA,是当前文件addA.js私有的,其余文件不可见。若是想在多个文件中分享变量a,必须定义为global对象的属性:ajax
1 global.a = 1;
这样咱们就能在其余的文件中访问变量a了,但这种写法不可取,输出模块对象最好的方式是module.exports:编程
1 // addA.js 2 var a = 1; 3 var addA = function(value) { 4 return value + x; 5 } 6 module.exports.addA = addA;
上面代码经过module.exports对象输出了一个函数,该函数就是模块外部与内部通讯的桥梁。加载模块须要使用require方法,该方法读取一个文件并执行,最后返回文件内部的module.exports对象。数组
1 var example = require('./addA.js'); 2 console.log(example.addA(1)); //2
CommonJs看起来是一个很不错的选择,拥有模块化所须要的严格的入口和出口,看起来一切都很美好,但它的一个特性却决定了它只能在服务器端大规模使用,而在浏览器端发挥不了太大的做用,那就是同步!这在服务器端不是什么问题,但放在浏览器端就出现问题了,由于文件都放在服务器上,若是网速不够快的话,前面的文件若是没有加载完成,浏览器就会失去响应!所以为了在浏览器上也实现模块化得来个异步的模块化才行!根据这个需求,咱们的下一位主角——AMD就产生了!浏览器
AMD的全名叫作:Asynchronous Module Definition即异步模块定义。它采用了异步的方式来加载模块,而后在回调函数中执行主逻辑,所以模块的加载不影响它后面的模块的运行。它的规范以下:服务器
1 define(id?, dependencies?, factory);
具体分析AMD咱们经过require.js来进行。require.js是一个很是小巧的JavaScript模块载入框架,是AMD规范最好的实现者之一,require.js的出现主要是来解决两个问题:框架
使用require.js咱们首先要加载它,为了不浏览器未响应,咱们在后面能够加上async,告诉浏览器这个文件须要异步加载(IE不支持该属性,因此须要把defer也加上):异步
1 <script src="js/require.js" defer async="true" ></script>
定义模块时,在require.js中咱们可使用define,但define对于须要定义的模块是不是独立的模块的写法是不一样;所谓的独立模块就是指不依赖于其余模块的模块,而非独立模块就是指不依赖于其余模块的模块。
define在定义独立模块时有两种写法,一种是直接定义对象;另外一种是定义一个函数,在函数内的返回值就是输出的模块了:
1 define({ 2 method1: function() {}, 3 method2: function() {}, 4 }); 5 //等价于 6 define(function () { 7 return { 8 method1: function() {}, 9 method2: function() {}, 10 } 11 });
若是define定义非独立模块,那么它的语法就规定必定是这样的:
1 define(['module1', 'module2'], function(m1, m2) { 2 3 return { 4 method: function() { 5 m1.methodA(); 6 m2.methodB(); 7 } 8 } 9 10 });
define在这个时候接受两个参数,第一个参数是module是一个数组,它的成员是咱们当前定义的模块所依赖的模块,只有顺利加载了这些模块,咱们新定义的模块才能成功运行。第二个参数是一个函数,当前面数组内的成员所有加载完以后它才运行,它的参数m与前面的module是一一对应的。这个函数必须返回一个对象,以供其余模块调用,须要注意的是,回调函数必须返回一个对象,这个对象就是你定义的模块。
在加载模块方面,AMD和CommonJs都是使用require。require.js也一样如此,它要求两个参数:module,callback:
require([module], callback);
第一个参数[module],是一个数组,里面的成员就是须要加载的模块;第二个参数callback,则是加载成功以后的回调函数。
require方法自己也是一个对象,它带有一个config方法,用来配置require.js运行参数。config方法接受一个对象做为参数。
1 //别名配置 2 requirejs.config({ 3 paths: { 4 jquery: [ //若是第一个路径不能完成加载,就调到第二个路径继续进行加载 5 '//cdnjs.cloudflare.com/ajax/libs/jquery/2.0.0/jquery.min.js', 6 'lib/jquery' //本地文件中不须要写.js 7 ] 8 } 9 }); 10 11 //引入模块,用变量$表示jquery模块 12 requirejs(['jquery'], function ($) { 13 $('body').css('background-color','black'); 14 });
虽然require.js实现了异步的模块化,但它仍然有一些不足的地方,在使用require.js的时候,咱们必需要提早加载全部的依赖,而后才可使用,而不是须要使用时再加载,使得初次加载其余模块的速度较慢,提升了开发成本。
CMD的全称是Common Module Definition,即通用模块定义。它是由蚂蚁金服的前端大佬——玉伯提出来的,实现的JavaScript库为sea.js。它和AMD的require.js很像,但加载方式不一样,它是按需就近加载的,而不是在模块的开始所有加载完成。它有如下两大核心特色:
在CMD规范中,一个文件就是一个模块,代码书写的格式是这样的:
define(factory);
当factory为函数时,表示模块的构造方法,执行该方法,能够获得该模块对外提供的factory接口,factory 方法在执行时,默认会传入三个参数:require、exports 和 module:
1 // 全部模块都经过 define 来定义 2 define(function(require, exports, module) { 3 4 // 经过 require 引入依赖 5 var $ = require('jquery'); 6 var Spinning = require('./spinning'); 7 8 // 经过 exports 对外提供接口 9 exports.doSomething = ... 10 11 // 或者经过 module.exports 提供整个接口 12 module.exports = ... 13 14 });
它与AMD的具体区别其实咱们也能够经过代码来表现出来,AMD须要在模块开始前就将依赖的模块加载出来,即依赖前置;而CMD则对模块按需加载,即依赖就近,只有在须要依赖该模块的时候再require就好了:
1 // AMD规范 2 define(['./a', './b'], function(a, b) { // 依赖必须一开始就写好 3 a.doSomething() 4 // 此处略去 100 行 5 b.doSomething() 6 ... 7 }); 8 // CMD规范 9 define(function(require, exports, module) { 10 var a = require('./a') 11 a.doSomething() 12 // 此处略去 100 行 13 var b = require('./b') 14 // 依赖能够就近书写 15 b.doSomething() 16 // ... 17 });
须要注意的是Sea.js的执行模块顺序也是严格按照模块在代码中出现(require)的顺序。
从运行速度的角度来说,AMD虽然在第一次使用时较慢,但在后面再访问时速度会很快;而CMD第一次加载会相对快点,但后面的加载都是从新加载新的模块,因此速度会慢点。总的来讲, require.js的作法是并行加载全部依赖的模块, 等完成解析后, 再开始执行其余代码, 所以执行结果只会"停顿"1次, 而Sea.js在完成整个过程时则是每次须要相应模块都须要进行加载,这期间会停顿是屡次的,所以require.js从总体而言相对会比Sea.js要快一些。