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》


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM