文件路徑
首先先搞清楚文件路徑的寫法,這裏我總是記不住,有點暈,正好這次整理一下。
- 以
/
爲起始,表示從根目錄開始解析; - 以
./
爲起始,表示從當前目錄開始解析; - 以
../
爲起始,表示從上級目錄開始解析;
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。