CommonJS、requirejs、ES6的對比

原文鏈接:https://www.cnblogs.com/gluncle/p/8392277.html

文件路徑

首先先搞清楚文件路徑的寫法,這裏我總是記不住,有點暈,正好這次整理一下。

  • 以 / 爲起始,表示從根目錄開始解析;
  • 以 ./ 爲起始,表示從當前目錄開始解析;
  • 以 ../ 爲起始,表示從上級目錄開始解析;

CommonJS

CommonJS是nodejs也就是服務器端廣泛使用的模塊化機制。 
該規範的主要內容是,模塊必須通過module.exports 導出對外的變量或接口,通過 require() 來導入其他模塊的輸出到當前模塊作用域中。

根據這個規範,每個文件就是一個模塊,有自己的作用域,文件中的變量、函數、類等都是對其他文件不可見的。

如果想在多個文件分享變量,必須定義爲global對象的屬性。(不推薦)

定義模塊

在每個模塊內部,module變量代表當前模塊。它的exports屬性是對外的接口,將模塊的接口暴露出去。其他文件加載該模塊,實際上就是讀取module.exports變量。

var x = 5;
var addX = function (value) {
  return value + x;
};
module.exports.x = x;
module.exports.addX = addX;

 

加載模塊

require方法用於加載模塊,後綴名默認爲.js

var app = require('./app.js');

模塊加載的順序,按照其在代碼中出現的順序

根據參數的不同格式,require命令去不同路徑尋找模塊文件。

  • 如果參數字符串以“/”開頭,則表示加載的是一個位於絕對路徑的模塊文件。
  • 如果參數字符串以“./”開頭,則表示加載的是一個位於相對路徑的模塊文件
  • 如果參數字符串不以“./“或”/“開頭,則表示加載的是一個默認提供的核心模塊(node核心模塊,或者通過全局安裝或局部安裝在node_modules目錄中的模塊)

入口文件

一般都會有一個主文件(入口文件),在index.html中加載這個入口文件,然後在這個入口文件中加載其他文件。

可以通過在package.json中配置main字段來指定入口文件。

模塊緩存

第一次加載某個模塊時,Node會緩存該模塊。以後再加載該模塊,就直接從緩存取出該模塊的module.exports屬性。

加載機制

CommonJS模塊的加載機制是,輸入的是被輸出的值的拷貝。也就是說,一旦輸出一個值,模塊內部的變化就影響不到這個值。

AMD

AMD(異步模塊定義)是爲瀏覽器環境設計的,因爲 CommonJS 模塊系統是同步加載的,當前瀏覽器環境還沒有準備好同步加載模塊的條件。

requirejs即爲遵循AMD規範的模塊化工具。 
RequireJS的基本思想是,通過define方法,將代碼定義爲模塊;通過require方法,實現代碼的模塊加載。

定義模塊

define方法用於定義模塊,RequireJS要求每個模塊放在一個單獨的文件裏。

按照是否依賴其他模塊,可以分成兩種情況討論。第一種情況是定義獨立模塊,即所定義的模塊不依賴其他模塊;第二種情況是定義非獨立模塊,即所定義的模塊依賴於其他模塊。

獨立模塊

define(function(){
    ……
    return {
        //返回接口
    }
})

define定義的模塊可以返回任何值,不限於對象。

非獨立模塊

define(['module1','module2'],function(m1,m2){
    ……
    return {
        //返回接口
    }
})

 

要定義的模塊依賴於module1和module2,那麼第一個參數就是依賴的模塊的數組。 
第二個參數是一個函數,僅當依賴的模塊都加載成功後纔會被調用。此函數的參數m1,m2與前面數組中的依賴模塊一一對應。

此模塊必須返回一個對象,供其他模塊調用。

加載模塊

同樣使用require()方法來加載模塊,但由於是異步的,因此使用回調函數的形式。

require(['foo','bar'],function(foo,bar){
    ……
})

 

上面方法表示加載foo和bar兩個模塊,當這兩個模塊都加載成功後,執行一個回調函數。該回調函數就用來完成具體的任務。

require方法也可以用在define方法內部。

define(function(require){
     var otherModule = require('otherModule');
})

 

require方法允許添加第三個參數,即錯誤處理的回調函數。

require(
    [ "backbone" ], 
    function ( Backbone ) {
        return Backbone.View.extend({ /* ... */ });
    }, 
    function (err) {
        // ...
    }
);

 

配置

require方法本身也是一個對象,它帶有一個config方法,用來配置require.js運行參數。

require.config({
    paths: {
        jquery:'lib/jquery'
    }
});

 

  • paths:paths參數指定各個模塊的位置。這個位置可以是同一個服務器上的相對位置,也可以是外部網址。可以爲每個模塊定義多個位置,如果第一個位置加載失敗,則加載第二個位置。上面就是指定了jquery的位置,那麼就可以直接在文件中require(['jquery'],function($){})

  • shim:有些庫不是AMD兼容的,這時就需要指定shim屬性的值。shim可以理解成“墊片”,用來幫助require.js**加載非AMD規範的庫**。

require.config({
    paths: {
        "backbone": "vendor/backbone",
        "underscore": "vendor/underscore"
    },
    shim: {
        "backbone": {
            deps: [ "underscore" ],
            exports: "Backbone"
        },
        "underscore": {
            exports: "_"
        }
    }
});

 

使用

在主頁面index.html中先通過script標籤引入require.min.js。 
再通過script標籤引入一個入口文件main.js,此入口文件一般用於配置(require.config),以及引入其他模塊。

CommonJS與AMD

  • CommonJS規範加載模塊是同步的,也就是說,只有加載完成,才能執行後面的操作。
  • AMD規範則是異步加載模塊,允許指定回調函數,在回調函數中執行操作。

由於Node.js主要用於服務器編程,模塊文件一般都已經存在於本地硬盤,所以加載起來比較快,不用考慮非同步加載的方式,所以CommonJS規範比較適用。但是,如果是瀏覽器環境,要從服務器端加載模塊,這時就必須採用非同步模式,因此瀏覽器端一般採用AMD規範。

AMD規範允許輸出的模塊兼容CommonJS規範,這時define方法需要寫成下面這樣:

define(function(require,exports,module){
    var someModule = require("someModule");
    var anotherModule = require("anotherModule");
    ……
    exports.asplode = function(){

    }
})

 

ES6 Modules

ES6正式提出了內置的模塊化語法,我們在瀏覽器端無需額外引入requirejs來進行模塊化。

ES6中的模塊有以下特點:

  • 模塊自動運行在嚴格模式下
  • 在模塊的頂級作用域創建的變量,不會被自動添加到共享的全局作用域,它們只會在模塊頂級作用域的內部存在;
  • 模塊頂級作用域的 this 值爲 undefined
  • 對於需要讓模塊外部代碼訪問的內容,模塊必須導出它們

定義模塊

使用export關鍵字將任意變量、函數或者類公開給其他模塊。

//導出變量
export var color = "red";
export let name = "cz";
export const age = 25;

//導出函數
export function add(num1,num2){
    return num1+num2;
}

//導出類
export class Rectangle {
    constructor(length, width) {
        this.length = length;
        this.width = width;
    }
}

function multiply(num1, num2) {
    return num1 * num2;
}

//導出對象,即導出引用
export {multiply}

 

重命名模塊

重命名想導出的變量、函數或類的名稱

function sum(num1, num2) {
    return num1 + num2;
}

export {sum as add}

 

這裏將本地的sum函數重命名爲add導出,因此在使用此模塊的時候必須使用add這個名稱。

導出默認值

模塊的默認值是使用 default 關鍵字所指定的單個變量、函數或類,而你在每個模塊中只能設置一個默認導出。

export default function(num1, num2) {
    return num1 + num2;
}

 

此模塊將一個函數作爲默認值進行了導出, default 關鍵字標明瞭這是一個默認導出。此函數並不需要有名稱,因爲它就代表這個模塊自身。對比最前面使用export導出的函數,並不是匿名函數而是必須有一個名稱用於加載模塊的時候使用,但是默認導出則無需一個名字,因爲模塊名就代表了這個導出值。

也可以使用重命名語法來導出默認值。

function sum(num1, num2) {
    return num1 + num2;
}

export { sum as default };

 

加載模塊

在模塊中使用import關鍵字來導入其他模塊。 
import 語句有兩個部分,一是需要導入的標識符,二是需導入的標識符的來源模塊。此處是導入語句的基本形式:

import { identifier1,identifier2 } from "./example.js"
  • 大括號中指定了從給定模塊導入的標識符
  • from指明瞭需要導入的模塊。模塊由一個表示模塊路徑的字符串來指定。

當從模塊導入了一個綁定時,你不能在當前文件中再定義另一個同名變量(包括導入另一個同名綁定),也不能在對應的 import 語句之前使用此標識符,更不能修改它的值。

導入單個綁定

如果一個模塊只導出了一個函數(或變量或類),或者導出了多個接口但是隻選擇導入其中的一個,那麼就可以寫成下面單個導入的模式:

import {sum} from './example.js'

導入多個綁定

從一個模塊中導入多個綁定:

import {sum,multiply} from './example.js'

完全導入一個模塊

還有一種情況,就是將整個模塊當做單一對象導入,該模塊的所有導出都會作爲對象的屬性存在:

import * as example from './example.js'
example.sum(1,2);
example.multiply(2,3);

 

在此代碼中, example.js 中所有導出的綁定都被加載到一個名爲 example 的對象中,具名導出( sum() 函數、 multiple() 函數)都成爲 example 的可用屬性。 
這種導入格式被稱爲命名空間導入,這是因爲該 example 對象並不存在於 example.js 文件中,而是作爲一個命名空間對象被創建使用,其中包含了 example.js 的所有導出成員。

然而要記住,無論你對同一個模塊使用了多少次 import 語句,該模塊都只會被執行一次。

在導出模塊的代碼執行之後,已被實例化的模塊就被保留在內存中,並隨時都能被其他 import 所引用.

import { sum } from "./example.js";
import { multiply } from "./example.js";
import { magicNumber } from "./example.js";

 

儘管此處的模塊使用了三個 import 語句,但 example.js 只會被執行一次。若同一個應用中的其他模塊打算從 example.js 導入綁定,則那些模塊都會使用這段代碼中所用的同一個模塊實例。

重命名導入

與導出相同,我們同樣可以重命名導入的綁定:

import { sum as add} from './example.js'
  • 1

導入默認值

如果一個模塊導出了默認值,那麼可以這樣導入默認值:

import sum from "./example.js";

這個導入語句從 example.js 模塊導入了其默認值。注意此處並未使用花括號,與之前在非默認的導入中看到的不同。本地名稱 sum 被用於代表目標模塊所默認導出的函數,因此無需使用花括號。

如果一個模塊既導出了默認值、又導出了一個或更多非默認的綁定的模塊:

export let color = "red";

export default function(num1, num2) {
    return num1 + num2;
}

 

可以像下面這樣使用一條import語句來導入它的所有導出綁定:

import sum,{color} from "./example.js"

 

逗號將默認的本地名稱與非默認的名稱分隔開,後者仍舊被花括號所包裹。 
要記住在 import 語句中默認名稱必須位於非默認名稱之前。

導入的再導出

有時想在當前的模塊中將已導入的內容再導出去,可以像下面這樣寫:

import {sum} from './example.js'
……
export {sum}

 

但是有一種更簡潔的方法:

export {sum} from './example.js'

同樣可以重命名:

export { sum as add } from "./example.js";

也可以使用完全導出:

export * from "./example.js";

限制

export 與 import 都有一個重要的限制,那就是它們必須被用在其他語句或表達式的外部,而不能使用在if等代碼塊內部。原因之一是模塊語法需要讓 JS 能靜態判斷需要導出什麼,正因爲此,你只能在模塊的頂級作用域使用 export與import。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章