前端模块化四大模块(学习记录)

71 阅读4分钟

一、CommonJS规范(Node.js)

CommonJS用同步的方式加载模块,在服务端,模块文件都存在本地磁盘,读取非常快,所有node.js是CommonJS规范的主要实践者,但在浏览器端受网络影响,更适合用异步加载的方案。 CommonJS规范有四个重要的环境变量为模块化的实现提供支持:module、exports、require、global。在实际使用时,用module.exports定义当前模块对外的输出的接口(不推荐直接用exports),用require加载模块。

// 定义模块math.js 
var basicNum = 0;
function add(a, b) { 
    return a + b; 
} 
module.exports = { 
    //在这里写上需要向外暴露的函数、变量 
    add: add, 
    basicNum: basicNum 
} 

// 引用自定义的模块时,参数包含路径,可省略.js
var math = require('./math'); 
math.add(2, 5); 

// 引用核心模块时,不需要带路径 
var http = require('http');
http.createService(...).listen(3000);

exports和module.exports的区别

exports = module.exports = {} exports可以看成module.exports的一种简化,本质上输出是一样的,但是对exports或者module.exports本身进行赋值,都会造成其间的连接中断

//在a.js中写入该代码
module.exports.userName = "赵大胖";
exports.age = 21;
exports = {};
exports.height = 169;
//在b.js中引入自定义模块a.js
var result = require("./a.js");  //假定两个文件是在同个目录下的
console.log(result);

//最后的打印结果为{userName:"赵大胖",age:21}
//原因如下:
/*
	(1)在第1、2行代码里,exports和module.exports向同一个对象里添加了两个属性,userName和age
	(2)在第3、4行代码,exports重新指向了一个新的对象,并给这个对象里添加了一个新的属性height
	(3)由于最后返回的是module.exports指向的对象,所以最后返回的是{userName:"赵大胖",age:22}
*/

在node中推荐使用module.exports,因为module.exports才是node自带的,exports是commonJS提供的

二、AMD规范(require.js)

AMD规范采用异步方式加载模块,所有依赖这个模块的语句都定义在一个回调函数中,等到加载完成后,这个回调函数才会运行。AMD是由JavaScript社区提出的专注于支持浏览器端模块化的标准。

使用define定义导出模块:

define('getSum', ['calculator'], function(math) {
    return function(a, b) {
        console.log('sum' + calculator.add(a, b))
    }
})

define接收三个参数,参数说明如下:

  1. 当前模块的id,相当于模块名
  2. 当前模块的依赖
  3. 模块的导出值,可以是函数或者对象,如果是函数导出的就是函数的返回值,如果是对象直接导出对象本身

使用require函数加载模块:

require(['getSum'], function(getSum) {
    getSum(2, 3)
})

require接收2个参数,参数说明如下:

  1. 指定加载的模块
  2. 当加载完成后执行的回调函数

AMD存在的缺点:

  1. 与同步加载的模块标准相比语法更加冗长
  2. 异步加载方式不明显,容易造成回调地狱,在目前的实际应用中逐渐减少

三、CMD规范(sea.js)

CMD与AMD的语法类似,但是CMD通过按需加载的方式,而不是必须在模块开始加载所有的依赖 CMD推崇依赖就近,延迟执行。AMD是预加载,CMD是懒加载

  • 优点:同样实现了浏览器端的模块加载,可以按需加载,依赖就近
  • 缺点:依赖SPM打包,模块的加载逻辑
/** AMD写法 **/
define(["a", "b", "c", "d", "e", "f"], function(a, b, c, d, e, f) { 
     // 等于在最前面声明并初始化了要用到的所有模块
    a.doSomething();
    if (false) {
        // 即便没用到某个模块 b,但 b 还是提前执行了
        b.doSomething()
    } 
});

/** CMD写法 **/
define(function(require, exports, module) {
    var a = require('./a'); //在需要时申明
    a.doSomething();
    if (false) {
        var b = require('./b');
        b.doSomething();
    }
});

/** sea.js **/
// 定义模块 math.js
define(function(require, exports, module) {
    var $ = require('jquery.js');
    var add = function(a,b){
        return a+b;
    }
    exports.add = add;
});
// 加载模块
seajs.use(['math.js'], function(math){
    var sum = math.add(1+2);
});

四、ES6模块化

在ES6中使用export导出模块,使用import引入模块,还提供了export default为模块指定默认输出,对应使用import时不需要大括号

/** 定义模块 math.js **/
var basicNum = 0;
var add = function (a, b) {
    return a + b;
};
export { basicNum, add };

/** 引用模块 **/
import { basicNum, add } from './math';
function test(ele) {
    ele.textContent = add(99 + basicNum);
}

/** export default **/
//定义输出
export default { basicNum, add };
//引入
import math from './math';
function test(ele) {
    ele.textContent = math.add(99 + math.basicNum);
}

因为ES6无法在浏览器中执行,所有需要通过babel将不被支持的import编译为能够支持的require

CommonJS和ES6的区别

  • CommonJS模块输出的是一个值的拷贝,ES6模块输出的是值的引用
  • CommonJS模块是运行时加载,ES6是编译时输出接口
  • CommonJS模块的require是同步加载模块的,ES6模块的import命令是异步加载的,有一个独立的模块依赖的解析阶段