CommonJS、requirejs、ES6的對比

 

文件路徑

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

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

CommonJS

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

根據這個規範,每一個文件就是一個模塊,有本身的做用域,文件中的變量、函數、類等都是對其餘文件不可見的。css

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

定義模塊

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

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

 

加載模塊

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

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

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

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

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

入口文件

通常都會有一個主文件(入口文件),在index.html中加載這個入口文件,而後在這個入口文件中加載其餘文件。es6

能夠經過在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。

相關文章
相關標籤/搜索