JavaScript-模組化
簡介
- 單一程式切割成多個檔案,每個程式檔案被稱為模組
- 模組有各自獨立的名稱空間,互不影響(可以在不同模組命名相同的變數名稱)
- 在開發大型複雜應用程式時,將重複性高的程式碼抽出模組化,更為方便且易於管理
script 要加上 type=“module” , 引入檔案的副檔名記得加上
.js
<script type="module">import message from "./message.js";</script>
Common JS
- 主要用於 Node.js(伺服器環境)
- 在 ES6 之前,JS 模組化的非官方社群規範
用法
- 一個
.js
是一個模組 - 使用
module.exports
輸出整個模組或某個功能 - 使用
exports
來輸出多個值 - 使用
require
函數來載入模組
範例
- 使用
module.exports
輸出
// math.js
function sum(a, b) {
return a + b
}
function multiply(a, b) {
return a * b
}
module.exports = {
sum,
multiply
}
- 使用
exports
新增新的屬性並賦值
// math.js
function sum(a, b) {
return a + b
}
function multiply(a, b) {
return a * b
}
exports.sum = sum
exports.multiply = multiply
- 使用
require
引入
// main.js
const math = require('./math.js')
console.log(math.sum(1, 2)) // 輸出 3
console.log(math.multiply(3, 4)) // 輸出 12
注意點
- CommonJS 模組是同步加載的
- 著當 Node.js 遇到 require() 語句時,會停下來等待模組加載和處理完成,然後才繼續執行後續代碼
不適合用於瀏覽器
- 當瀏覽器加載一個模組時,它會阻塞頁面的其他腳本執行,直到模組加載和處理完畢
- 會導致瀏覽器頁面渲染延遲,降低用戶體驗
ES Module
- 是 ES6 引入的官方標準化的 JavaScript 模組系統
- 可用於 Node.js(伺服器端)或是瀏覽器(客戶端)
用法
- 使用
import
關鍵字導入模組 - 使用
export
輸出模組 - 需要在
<script>
標籤上加上type = "module"
預設輸出
- 輸出時使用
default
關鍵字,輸入時可使用任意名稱 - 預設只能輸出一個東西
// data.js
function echo(msg){
console.log(msg)
}
let name ="這裡是data"
echo(name)
exprot default echo // export default 資料
// main.js
import echo from './data.js'
let name = '這裡是main'
echo(name)
//在HTML檔案中引入主要js檔案
<script type="module" src="main.js"></script>
- export 多個 function
//可以放在一個物件
// data.js
function echo(msg) {
console.log(msg)
}
function add(a, b) {
console.log(a + b)
}
//如果物件的屬性名稱和值名稱相同,可以省略屬性名稱,直接寫出值
// export default {
// echo:echo,
// add:add
// }
export default {
echo,
add
}
// main.js
//可以自行命名import
import data from './data.js'
console.log(data) // { echo:fn, add:fn }
data.echo('main') // main
data.add(2, 4) // 6
具名輸入、輸出
- 使用大括號包覆輸入、輸出的變數名稱
- 輸出與輸入的名稱必須相同
//輸出語法
export { 變數1, 變數2,... }
//輸入語法
import { 變數1, 變數2,... } from '模組檔案路徑'
export const a = 1
export const b = 2
import { a, b } from './xxx.js'
- 同時使用預設、非預設輸出
// lib.js
const x = 3
const obj = { x: 3, y: 6 }
const data = { name: 'Ron', age: 33 }
export default x
export { obj, data }
// 整合寫法
export { x as default, obj, data }
// main.js
import x from './lib.js'
import { obj, data } from './lib.js'
//整合寫法
import x, { obj, data } from './lib.js'
- 輸出多個 function 寫法
// lib.js
const add = (a, b) => {
console.log(a + b)
}
const multiply = (a, b) => {
console.log(a * b)
}
const math = { add, multiply }
export default math
// main.js
//載入所有功能 - 使用預設輸入
import math from './lib.js'
math.add(1, 2)
math.multiply(3, 4)
//載入個別功能
import { add } from './lib.js'
add(1, 2)
- 用*載入所有資料
import * as stringFunctions from './string_functions.js'
stringFunctions.uppercaseString('hello')
stringFunctions.lowercaseString('WORLD!')
靜態載入
- 一般使用
import
關鍵字,是靜態載入 - 在編譯階段,程式運行前就已經確定和載入要使用的模組
- 有利於優化,例如樹搖(tree-shaking)
動態載入
- 使用
import()
語法,回傳一個Promise
- 在程式執行階段進行,可以根據條件或某些運行後的計算來決定是否載入模組
- 可用於按需載入、代碼分割或懶加載
範例
// lib.js
// 寫法1
export function sayHello() {
return 'Hello World'
}
// 寫法2
export default function () {
return 'Hello World'
}
- 按下按鈕後才載入
lib.js
// main.js
document.getElementById('myButton').addEventListener('click', () => {
import('./lib.js')
.then((lib) => {
console.log(lib)
alert(lib.sayHello())
})
.catch((err) => {
console.error(err)
})
})
// 使用async await
document.getElementById('myButton').addEventListener('click', async () => {
const lib = await import('./lib.js')
alert(lib.sayHello())
})
// 寫法2
document.getElementById('myButton').addEventListener('click', async () => {
const lib = await import('./lib.js')
alert(lib.default())
})
CommonJS 和 ESModule 的差異
- CommonJS 模組輸出的是一個值的拷貝,ESModule 輸出的是值的引用
- CommonJS 模組是執行時載入,ESModule 模組是編譯時輸出介面
參考資料
# JavaScript # CommonJS # ESModule