js模块化CommonJS、ES2015、AMD、CMD对比与介绍

模块发展史:一开始人们认为js没什么用,官方定义的API只能构建基于浏览器的应用程序。CommonJS就按耐不住了,CommonJS API定义很多普通应用程序(主要指非浏览器的应用)使用的API,从而填补了这个空白。它的终极目标是提供一个类似Python,Ruby和Java标准库。这样的话,开发者可以使用CommonJS API编写应用程序,然后这些应用可以运行在不同的JavaScript解释器和不同的主机环境中。

2009年,美国程序员Ryan Dahl创造了node.js项目,将javascript语言用于服务器端编程。这标志"Javascript模块化编程"正式诞生。因为老实说,在浏览器环境下,没有模块也不是特别大的问题,毕竟网页程序的复杂性有限;但是在服务器端,一定要有模块,与操作系统和其他应用程序互动,否则根本没法编程。NodeJS是CommonJS规范的实现,webpack 也是以CommonJS的形式来书写。

基于commonJS规范的nodeJS出来以后,服务端的模块概念已经形成,很自然地,大家就想要客户端模块。而且最好两者能够兼容,一个模块不用修改,在服务器和浏览器都可以运行。但是,由于一个重大的局限,使得CommonJS规范不适用于浏览器环境。还是上面的代码,如果在浏览器中运行,会有一个很大的问题,你能看出来吗?

	 var math = require('math');
	 math.add(2, 3);

第二行math.add(2, 3),在第一行require(‘math’)之后运行,因此必须等math.js加载完成。也就是说,如果加载时间很长,整个应用就会停在那里等。您会注意到 require 是同步的

这对服务器端不是一个问题,因为所有的模块都存放在本地硬盘,可以同步加载完成,等待时间就是硬盘的读取时间。但是,对于浏览器,这却是一个大问题,因为模块都放在服务器端,等待时间取决于网速的快慢,可能要等很长时间,浏览器处于"假死"状态。

因此,浏览器端的模块,不能采用"同步加载"(synchronous),只能采用"异步加载"(asynchronous)。这就是AMD规范诞生的背景。

CommonJS是主要为了JS在后端的表现制定的,他是不适合前端的,AMD(异步模块定义)出现了,它就主要为前端JS的表现制定规范。

一、commonJS
1.著名实现:NodeJS、Webpack
2.模块规范
一个文件就是一个模块,拥有单独的作用域。普通方式定义的变量、函数、对象都属于该模块内。

  • 通过require加载模块
  • 通过 exportsmodul.exports 来暴露模块中的内容

注:{模块引用(require)} {模块定义(exports)} {模块标识(module)}
require()用来引入外部模块;exports对象用于导出当前模块的方法或变量,唯一的导出口;module对象就代表模块本身。

3.使用 exports 暴露模块接口

(1)下面我们在 Node.js 中创建一个模块,文件名为:handle.js

   exports.hello = function() {
	  console.log('Hello CommonJS');
	}

(2)创建一个main.js文件,引入这个模块并调用

var handle = require('./handle');
handle.hello();

(3)控制台输入node main.js,运行结果:Hello CommonJS

4.使用 modul.exports 暴露模块对象

(1)把一个对象封装到模块中,名为handle.js

//私有变量
var test = 110;
 
//公开方法
function Handle() {
    var name;
    this.setName = function(Name) {
        name = Name;
    };
    this.hello = function() {
        console.log('Hello ' + name);
    };
};
 
module.exports = Handle;

(2).创建一个 main.js 文件,引入这个模块并调用

var Handle = require('./handle');
var hello = new Handle();
hello.setName('commonJS');
hello.hello();

(3)控制台输入node main.js,运行结果:Hello CommonJS
5.原理
浏览器不兼容CommonJS的根本原因,在于缺少四个Node.js环境的变量

  • module
  • exports
  • require
  • global

只要能够提供这四个变量,浏览器就能加载 CommonJS 模块

示例:

var module = {
  exports: {}
};

(function(module, exports) {
  exports.multiply = function (n) { return n * 1000 };
}(module, module.exports))

var f = module.exports.multiply;
f(5) // 5000 

二、ES2015
1.2015年6月,ES2015(ECMAScript6、ES6)正式发布
2.模块规范

  • 一个模块就是一个独立的文件。该文件内部的所有变量,外部无法获取
  • export 命令用于规定模块的对外接口
  • import 命令用于输入其他模块提供的功能

3.使用 export命令规定对外接口
(1)下面我们在 Node.js 中创建一个模块,文件名为:handle.js

//圆面积计算
export function area(radius) {
  return Math.PI * radius * radius;
}
 
//圆周长计算
export function circumference(radius) {
  return 2 * Math.PI * radius;
}

(2)创建一个 main.js 文件,引入这个模块并调用

import {area,circumference} from './handle';
console.log('圆面积:' + area(10));
console.log('圆周长:' + circumference(11));

当然也可以使用星号(*)指定一个对象,实现模块的整体加载

import * as circle from './handle';
console.log('圆面积:' + circle.area(10));
console.log('圆周长:' + circle.circumference(11));

(3)由于 NodeJS 目前还不支持 ES2015 的 Module,这里我们借助 babel-node 来执行

4.使用 export default 命令来输出模块

(1)下面我们使用 export default 命令用于指定模块的默认输出。模块文件名为:handle.js

//圆面积计算(作为默认接口)
export default function(radius) {
  return Math.PI * radius * radius;
}
 
//圆周长计算
export function circumference(radius) {
  return 2 * Math.PI * radius;
}

(2)创建一个 main.js 文件,引入这个模块并调用。注意:对于 export default指定模块的默认输出,import 语句不需要使用大括号

import area, {circumference} from './handle';
console.log('圆面积:' + area(10));
console.log('圆周长:' + circumference(11));

(3)同样使用babel-node运行

三、AMD

1.基本介绍

  • AMD 全称为 Asynchromous Module Definition(异步模块定义)
  • AMDRequireJS 在推广过程中对模块定义的规范化产出,它是一个在浏览器端模块化开发的规范
  • require.js的诞生,就是为了解决这两个问题:
    1. 实现js文件的异步加载,避免网页失去响应;
    2. 管理模块之间的依赖性,便于代码的编写和维护。
  • AMD 模式可以用于浏览器环境并且允许非同步加载模块,也可以按需动态加载模块

2.模块规范

  • AMD 通过异步加载模块。模块加载不影响后面语句的运行。所有依赖某些模块的语句均放置在回调函数中

  • AMD 规范只定义了一个函数 define,通过 define 方法定义模块。该函数的描述如下:

      define(id?, dependencies?, factory)
      id:指定义中模块的名字(可选)。如果没有提供该参数,模块的名字应该默认为模块加载器请求的指定脚本的名字。如果提供了该参数,模块名必须是“顶级”的和绝对的(不允许相对名字)。
      dependencies:当前模块依赖的,已被模块定义的模块标识的数组字面量(可选)。
      factory:一个需要进行实例化的函数或者一个对象。
    
  • AMD 规范允许输出模块兼容 CommonJS 规范,这时 define 方法如下:

      define(function (require, exports, module) {
          var reqModule = require("./someModule");
          requModule.test();
            
          exports.asplode = function () {
              //someing
          }
      });
    
  • require([module], callback);//

3.独立模块

(1)我们使用 RequireJS 定义一个不依赖其他模块得独立模块,文件名:handle.js

define(function(){
    var add = function(x,y) {
        return x + y;
    };
    return {
        add : add
    }
});

(2)接着创建一个 html 页面,其内部加载并调用这个模块

<!DOCTYPE html>
<html>
    <head>
        <script type="text/javascript" src="require.js"></script>
        <script type="text/javascript">
          require(['handle'], function (m){
            console.log(m.add(2,3));
          });
        </script>
    </head>
    <body>
    </body>
</html>

(3)控制台输出5

4.存在依赖的函数式定义

下面定义的模块又依赖于 cart 和 inventory 这两个模块(它们都处在同一个文件夹下)

define(["./cart", "./inventory"], function(cart, inventory) {
	        //return an object to define the "my/shirt" module.
	        return {
	            color: "blue",
	            size: "large",
	            addToCart: function() {
	                inventory.decrement(this);
	                cart.add(this);
	            }
	        }
	    }
	);

四、CMD

1.基本介绍
(1)CMD 全称为 Common Module Definition,它是国内玉伯大神在开发 SeaJS 的时候提出来的
(2)CMD 与 AMD 挺相近,二者区别如下:

  • 对于依赖的模块 CMD 是延迟执行,而 AMD 是提前执行
  • CMD 推崇依赖就近,AMD 推崇依赖前置
  • AMD 的 api 默认是一个当多个用,CMD 严格的区分推崇职责单一,其每个 API 都简单纯粹。例如:AMD 里 require 分全局的和局部的。CMD 里面没有全局的 require,提供 seajs.use() 来实现模块系统的加载启动

2.使用 exports 暴露模块接口
(1)下面使用 sea.js 创建一个模块,文件名为:handle.js

	define(function(require, exports) {
	    // 对外提供name属性
	    exports.name = 'hangge';
	    // 对外提供hello方法
	    exports.hello = function() {
	      console.log('Hello sea.js');
	    };
	});

(2)接着创建一个 html 页面,其内部加载并调用这个模块

	<!DOCTYPE html>
	<html>
	    <head>
	        <script type="text/javascript" src="sea.js"></script>
	        <script type="text/javascript">
	          //加载一个模块,在加载完成时,执行回调
	          seajs.use('handle', function(a) {
	            a.hello();
	          });
	        </script>
	    </head>
	    <body>
	    </body>
	</html>

(3)控制台输出: Hello sea.js
3.使用 modul.exports 暴露模块对象
(1)下面我们把一个对象封装到模块中,文件名为:handle.js

	define(function(require, exports, module) {
	    // 对外提供接口
	    module.exports = {
	        name: 'hangge',
	        hello: function() {
	          console.log('Hello sea.js');
	        }
	    };
	});

(2)接着创建一个 html 页面,其内部加载并调用这个模块

<!DOCTYPE html>
<html>
    <head>
        <script type="text/javascript" src="sea.js"></script>
        <script type="text/javascript">
          //加载一个模块,在加载完成时,执行回调
          seajs.use('handle', function(a) {
            a.hello();
          });
        </script>
    </head>
    <body>
    </body>
</html>

(3)控制台输出: Hello sea.js

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值