JS之AMD、CMD、CommonJS、ES6、UMD的使用笔记

前言

如下图:

AMD与CMD的主要区别:

  • 1. 对于依赖的模块,AMD 是提前执行,CMD 是延迟执行。不过 RequireJS 从 2.0 开始,也改成可以延迟执行(根据写法不同,处理方式不同)。CMD 推崇 as lazy as possible.
  • 2. CMD 推崇依赖就近,AMD 推崇依赖前置。看代码:
// CMD
define(function(require, exports, module) {
    var a = require('./a')
    a.doSomething()
    var b = require('./b')
    b.doSomething()
})
// AMD 默认推荐的是
define(['./a', './b'], function(a, b) { // 依赖必须一开始就写好  
    a.doSomething()
    b.doSomething()
})

AMD与CMD的其它区别可参考地址:https://www.zhihu.com/question/20351507

AMD

AMD规范可参考地址:https://github.com/amdjs/amdjs-api/wiki/AMD  

AMD 是 RequireJS 在推广过程中对模块定义的规范化产出。

根据AMD规范,我们可以使用define定义模块,使用require调用模块,语法:

 define(id?, dependencies?, factory);
  • id: 定义中模块的名字;可选;如果没有提供该参数,模块的名字应该默认为模块加载器请求的指定脚本的名字;
  • dependencies:依赖的模块;
  • factory:工厂方法,返回定义模块的输出值。

总结一段话:声明模块的时候指定所有的依赖dependencies,并且还要当做形参传到factory中,对于依赖的模块提前执行,依赖前置

例子1:

 define("alpha", ["require", "exports", "beta"], function (require, exports, beta) {
       exports.verb = function() {
           return beta.verb();
           //Or:
           return require("beta").verb();
       }
   });

例子2:

 define(["alpha"], function (alpha) {
       return {
         verb: function(){
           return alpha.verb() + 2;
         }
       };
   });

例子3:

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

例子4:

define(function (require, exports, module) {
     var a = require('a'),
         b = require('b');

     exports.action = function () {};
   });

使用require函数加载模块:

require([dependencies],function(){});
  • 第一个参数是一个数组,表示所依赖的模块
  • 第二个参数是一个回调函数,当前面指定的模块都加载成功后,它将被调用.加载的模块会以参数形式传入该函数,从而在回调函数内部就可以使用这些模块

require()函数在加载依赖的函数的时候是异步加载的,这样浏览器不会失去响应,它指定的回调函数,只有前面的模块都加载成功后,才会运行,解决了依赖性的问题

require(['alpha'],function(alpha){
    alpha.verb ();
})

CMD

CMD规范参考地址:https://github.com/seajs/seajs/issues/242  CMD 是 SeaJS 在推广过程中对模块定义的规范化产出。

define Function

define 是一个全局函数,用来定义模块。

define define(factory)

define 接受 factory 参数,factory 可以是一个函数,也可以是一个对象或字符串。

factory 为对象、字符串时,表示模块的接口就是该对象、字符串。比如可以如下定义一个 JSON 数据模块:

define({ "foo": "bar" });

也可以通过字符串定义模板模块:

define('I am a template. My name is {{name}}.');

factory 为函数时,表示是模块的构造方法。执行该构造方法,可以得到模块向外提供的接口。factory 方法在执行时,默认会传入三个参数:requireexports 和 module

define(function(require, exports, module) {

  // 模块代码

});

define define(id?, deps?, factory)

define 也可以接受两个以上参数。字符串 id 表示模块标识,数组 deps 是模块依赖。比如:

define('hello', ['jquery'], function(require, exports, module) {

  // 模块代码

});

id 和 deps 参数可以省略。省略时,可以通过构建工具自动生成。

注意:带 id 和 deps 参数的 define 用法不属于 CMD 规范,而属于 Modules/Transport 规范。

define.cmd Object

一个空对象,可用来判定当前页面是否有 CMD 模块加载器:

if (typeof define === "function" && define.cmd) {
  // 有 Sea.js 等 CMD 模块加载器存在
}

 require Function

require 是 factory 函数的第一个参数。

require require(id)

require 是一个方法,接受 模块标识 作为唯一参数,用来获取其他模块提供的接口。

define(function(require, exports) {

  // 获取模块 a 的接口
  var a = require('./a');

  // 调用模块 a 的方法
  a.doSomething();

});

require.async require.async(id, callback?)

require.async 方法用来在模块内部异步加载模块,并在加载完成后执行指定回调。callback 参数可选。

define(function(require, exports, module) {

  // 异步加载一个模块,在加载完成时,执行回调
  require.async('./b', function(b) {
    b.doSomething();
  });

  // 异步加载多个模块,在加载完成时,执行回调
  require.async(['./c', './d'], function(c, d) {
    c.doSomething();
    d.doSomething();
  });

});

注意:require 是同步往下执行,require.async 则是异步回调执行。require.async 一般用来加载可延迟异步加载的模块。

exports Object

exports 是一个对象,用来向外提供模块接口。

define(function(require, exports) {

  // 对外提供 foo 属性
  exports.foo = 'bar';

  // 对外提供 doSomething 方法
  exports.doSomething = function() {};

});

除了给 exports 对象增加成员,还可以使用 return 直接向外提供接口。

define(function(require) {

  // 通过 return 直接提供接口
  return {
    foo: 'bar',
    doSomething: function() {}
  };

});

module Object

module 是一个对象,上面存储了与当前模块相关联的一些属性和方法。

module.id String

模块的唯一标识。

define('id', [], function(require, exports, module) {

  // 模块代码

});

上面代码中,define 的第一个参数就是模块标识。

module.exports Object

当前模块对外提供的接口。

传给 factory 构造方法的 exports 参数是 module.exports 对象的一个引用。只通过 exports 参数来提供接口,有时无法满足开发者的所有需求。 比如当模块的接口是某个类的实例时,需要通过 module.exports 来实现:

define(function(require, exports, module) {

  // exports 是 module.exports 的一个引用
  console.log(module.exports === exports); // true

  // 重新给 module.exports 赋值
  module.exports = new SomeClass();

  // exports 不再等于 module.exports
  console.log(module.exports === exports); // false

});

CommonJS 

CommonJS是服务器端模块的规范,Node.js采用了这个规范.Node.JS首先采用了js模块化的概念.
CommonJS定义的模块分为:模块引用(require)/ 模块定义(exports)/模块标识(module)

所有代码都运行在模块作用域,不会污染全局作用域。

模块可以多次加载,但是只会在第一次加载时运行一次,然后运行结果就被缓存了,以后再加载,就直接读取缓存结果。要想让模块再次运行,必须清除缓存。

模块加载的顺序,按照其在代码中出现的顺序。

//common.js
module.exports = function(a, b) {
return a-b
}

let minus = require('./common.js')  //文件相对路径
console.log(minus(5,4)) 
// 结果: 1

详细可以参考《阮一峰:CommonJS规范》

ES6

导出

ES6两种导出方式:

  1. 命名导出
  2. 默认导出

命名导出

// 写法1
export const name = 'calculator';
export const add = function (a,b) {
    return a + b;
}

// 写法2
const name = 'calculator';
const add = function (a,b) {
    return a + b;
}
export {name, add};

在使用命名导出时,可以通过as关键字对变量重命名。如:

const name = 'calculator';
const add = function (a,b) {
    return a + b;
}
export {name, add as getSum};  // 在导入时即为name 和 getSum

默认导出

export default {
    name: 'calculator';, 
    add: function (a,b) {
        return a + b;
    }
}; 

导入

针对命名导出的模块,导入方式如下:

// calculator.js
const name = 'calculator';
const add = function (a,b) {
    return a + b;
}
export {name, add};

// 一般导入方式
import {name, add} from './calculator.js'
add(2,3);

// 通过as关键字对导入的变量重命名
import {name, add as calculateSum} from './calculator.js'
calculateSum(2,3);

// 使用 import * as <myModule>可以把所有导入的变量作为属性值添加到<myModule>对象中,从而减少了对当前作用域的影响
import * as calculateObj from './calculator.js'
calculateObj.add(2,3);

对于默认导出来说,import后面直接跟变量名,并且这个名字可以自由指定,它指代了calculator.js中默认导出的值。从原理上可以这样理解:

import {default as myCalculator} from './calculator.js'

还有两种导入方式混合起来使用的例子,如下:

// index.js
import React, {Component} from 'react';

注:这里的React必须写在大括号前面,而不能顺序颠倒,否则会提示语法错误。

 复合写法

在工程中,有时候需要把一个模块导入后立即导出,比如专门用来集合所有页面或组件的入口文件。此时可以采用复合形式的写法:

export {name, add } from './calculator.js'

复合写法只支持通过命名导出的方式暴露出来的变量,默认导出则没有对应的复合形式,只能将导入和导出拆分开。

import calculator from './calculator.js'
export default calculator;

 UMD

UMD并不能说是一种模块标准,不如说它是一组模块形式的集合更准确。UMD的全称是Universal Module Definition,也就是通用模块标准。它的目标是使一个模块能运行在各种环境下,不论是CommonJS、AMD,还是非模块化的环境(当时ES6 Module还未被提出)

(function(root, factory) {
    if (typeof define === 'function' && define.amd) {
        // AMD
        define(['jquery'], factory);
    } else if (typeof exports === 'object') {
        // Node, CommonJS之类的
        module.exports = factory(require('jquery'));
    } else {
        // 浏览器全局变量(root 即 window)
        root.returnExports = factory(root.jQuery);
    }
}(this, function($) {
    // 方法
    function myFunc() {};

    // 暴露公共方法
    return myFunc;

}));

 详细可参考:《可能是最详细的UMD模块入门指南》《AMD , CMD, CommonJS,ES Module,UMD》

posted @ 2020-07-08 22:28  风雨后见彩虹  阅读(1564)  评论(0编辑  收藏  举报