模組 Module
台灣翻譯叫做模組
,中國翻譯叫模塊
,但都是指 module
。
module
是用來幫助 developer 管理固定的功能,例如網站的登入頁面需要驗證使用者輸入的 email 是否符合格式,而在註冊頁面也同樣需要驗證 email 格式的功能,那就可以把這個功能寫在一個檔案裡面,當需要用到的時候就可以直接 import 使用,如此一來就可以避免在不同頁面都重複寫同樣的 code,當功能需要更改時也只要更改源頭檔案就好。
但在過去 JavaScript 沒有 module
的概念,所以 developer 社群開發非官方的模組使用方法:require
引入模組與 exports
匯出模組。
require
引入模組 與 exports
匯出模組
require
node.js
有許多內建模組,可以使用 require
引入模組並加以使用。
語法
const <self-defined name> = require('<filename>')
範例
var os = require('os');
// 前面變數名稱可以自己取名
// 後面 'os' 是要引入的 node.js 模組名稱
console.log(os.platform());
// 使用方法就像是物件一樣
// 直接變數名稱後面接 property 或 method 名
exports
除了引入別人寫好的模組外,自己也可以寫模組給自己用,只要將寫好的內容 exports
,在別的 js 檔案裡再 require
引入即可。
語法
exports.<self-defined name> = <component>
或
module.exports = <component>
範例
先 exports
寫好的功能
- 法一
function double(n) {
return n * 2
}
var obj = {
double: double,
triple: function(n) {
return n * 3
}
}
module.exports = obj
// 想要輸出哪個就直接接後面
// 可想成把 obj 變成 module.exports 這物件
- 法二
function double(n) {
return n * 2
}
exports.double = double
exports.triple = function(n) {
return n * 3
}
// 可想成 exports 本身就是個空物件 {}
// 只要把東西指定到物件裡就輸出了
再 require
引入使用
var myModule = require('./myModule')
// 引入自定義的 module 通常會放完整路徑
// 引入 module 可以想成把以下情況
// var myModule = {
// double: double,
// triple: function(n) {
// return n * 3
// }
// }
console.log(myModule.double(2), myModule.triple(10))
// 使用輸入的 module
// 得到 4 30
使用 ES6 的 export
與 import
ECMAScript 在 ES6 新增了 import
與 export
來達到 module 輸出及輸入的效果,語法相對於上述 require
與 exports
還要多樣,不過兩種方法都必須了解。
export
語法
// Exporting individual features
export let name1, name2, …, nameN; // also var, const
export let name1 = …, name2 = …, …, nameN; // also var, const
export function functionName(){...}
export class ClassName {...}
// Export list
export { name1, name2, …, nameN };
// Renaming exports
export { variable1 as name1, variable2 as name2, …, nameN };
// Exporting destructured assignments with renaming
export const { name1, name2: bar } = o;
// Default exports
export default expression;
export default function (…) { … } // also class, function*
export default function name1(…) { … } // also class, function*
export { name1 as default, … };
// Aggregating modules
export * from …; // does not set the default export
export * as name1 from …; // Draft ECMAScript® 2O21
export { name1, name2, …, nameN } from …;
export { import1 as name1, import2 as name2, …, nameN } from …;
export { default } from …;
import
語法
import defaultExport from "module-name";
import * as name from "module-name";
import { export } from "module-name";
import { export as alias } from "module-name";
import { export1 , export2 } from "module-name";
import { export1 , export2 as alias2 , [...] } from "module-name";
import defaultExport, { export [ , [...] ] } from "module-name";
import defaultExport, * as name from "module-name";
import "module-name";
上述為完整語法,若要簡單了解則可先知道 named export
以及 default export
兩種 export 方式:
1. named export
將寫好的 component 依照原本宣告的名稱 export
,再將該 component 按原本名稱 import
。此外,一個檔案中可以有多個 named export
。
若 named export 多個檔案,在 import 時可用 import * as <name>
一次 import 所有 component,要使用時就把它當作一個 object 來呼叫 <name>.<component>
。
範例
export
- 法一
const isAdult = (age) => age >= 18;
const canDrink = (age) => age >= 18;
export { isAdult, canDrink };
// 把要 export 的變數放在 {} 裡面
- 法二:可寫成 inline export
export const isAdult = (age) => age >= 18;
export const canDrink = (age) => age >= 18;
import
- 法一:
import './person.js'
// import 特定檔案
import { isAdult, canDrink } from './person.js'
// import 檔案的特定 component
console.log(isAdult(4));
console.log(canDrink(21));
- 法二:
import './person.js'
// import 特定檔案
import * as obj from './person.js'
// 以 import * as 一次引入所有 component
// 並把引入的 component 設為 obj 底下的 property/method
console.log(obj.isAdult(4));
console.log(obj.canDrink(21));
2. default export
一個檔案僅能有唯一的 default export
,export
時以 default export 的 component 不用給予特定名稱,且在 import
時可以設任何的名字來使用。
範例
export
- 法一
const square = (x) => x * x;
const add = (a, b) => a + b;
const subtract = (a, b) => a - b;
export { square, add, subtract as default };
- 法二
export const square = (x) => x * x;
export const add = (a, b) => a + b;
export default (a, b) => a - b;
import
import './utils.js';
import anythingIWant, { square, add } from './utils.js';
// 其中的 anythingIWant 就是 default 的 (a, b) => a - b
// 亦即輸出時設為 default 值,在輸入時可以設任何的名字來使用
console.log(square(4));
console.log(add(100, 23));
console.log(anythingIWant(100, 81));
總結差異:
- named export
- 同個檔案可以 named export
多個
component - export 和 import 時要以同樣的名字進行(import 後可 renamed)
- 在 import 時要以
{ }
包住 - 可用
import * as <name>
一次 import 所有 component
- 同個檔案可以 named export
- default export
- 同個檔案只能有
唯一
一個 component 是 default export - 在 import 時不用
{ }
也不用以同樣名字 import
- 同個檔案只能有
因為 default 一次只能設定一個,通常會將大的 component 設為 default
,而其他比較小的則用 named export
。
其他範例
export
export function add(a,b) {
return a + b
}
export const PI = 3.14
// 以上兩者 named export
import
import {add, PI} from './utils'
// 必須用 {} 包起來
console.log(add(3, 5), PI)
或
import * as whatever from './utils'
// import * 代表把所有東西都 import 進來
console.log(whatever.add(3, 5), whatever.PI)
// 使用時就要像取用 object 內容一樣
export
function add(a,b) {
return a + b
}
const PI = 3.14
export {add as addFunction, PI}
// export 時可 rename
import
import {addFunction as a, PI} from './utils'
// import 時同樣也可 rename
console.log(a(3, 5), PI)
export
export default function add(a, b) {
return a + b
}
// 用 default 來 export function 的話
// 可以不取名字(匿名函式)
export const PI = 3.14
import
import add, {PI} from 'utils'
// 用 default 來 export 的東西,
// 在 import 時不用 {} 包起來,
// 且可以直接取任意名稱
console.log(add(3, 5), PI)
參考
完全解析 JavaScript import、export
模組系統
JavaScript 中 require, import 的差別及效能
一文讓你徹底搞清楚javascript中的require、import與export
ES modules: A cartoon deep-dive
Brief history of JavaScript Modules