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.jsfunction sum(a, b) { return a + b}function multiply(a, b) { return a * b}module.exports = { sum, multiply}
- 使用
exports
新增新的屬性並賦值
// math.jsfunction sum(a, b) { return a + b}function multiply(a, b) { return a * b}exports.sum = sumexports.multiply = multiply
- 使用
require
引入
// main.jsconst math = require('./math.js')console.log(math.sum(1, 2)) // 輸出 3console.log(math.multiply(3, 4)) // 輸出 12
注意點
- CommonJS 模組是同步加載的
- 著當 Node.js 遇到 require() 語句時,會停下來等待模組加載和處理完成,然後才繼續執行後續代碼
不適合用於瀏覽器
- 當瀏覽器加載一個模組時,它會阻塞頁面的其他腳本執行,直到模組加載和處理完畢
- 會導致瀏覽器頁面渲染延遲,降低用戶體驗
ES Module
- 是 ES6 引入的官方標準化的 JavaScript 模組系統
- 可用於 Node.js(伺服器端)或是瀏覽器(客戶端)
用法
- 使用
import
關鍵字導入模組 - 使用
export
輸出模組 - 需要在
<script>
標籤上加上type = "module"
預設輸出
- 輸出時使用
default
關鍵字,輸入時可使用任意名稱 - 預設只能輸出一個東西
// data.jsfunction echo(msg){ console.log(msg)}let name ="這裡是data"echo(name)exprot default echo // export default 資料
// main.jsimport echo from './data.js'let name = '這裡是main'echo(name)//在HTML檔案中引入主要js檔案<script type="module" src="main.js"></script>
- export 多個 function
//可以放在一個物件// data.jsfunction 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//可以自行命名importimport data from './data.js'console.log(data) // { echo:fn, add:fn }data.echo('main') // maindata.add(2, 4) // 6
具名輸入、輸出
- 使用大括號包覆輸入、輸出的變數名稱
- 輸出與輸入的名稱必須相同
//輸出語法export { 變數1, 變數2,... }//輸入語法import { 變數1, 變數2,... } from '模組檔案路徑'
export const a = 1export const b = 2import { a, b } from './xxx.js'
- 同時使用預設、非預設輸出
// lib.jsconst x = 3const obj = { x: 3, y: 6 }const data = { name: 'Ron', age: 33 }export default xexport { obj, data }// 整合寫法export { x as default, obj, data }
// main.jsimport x from './lib.js'import { obj, data } from './lib.js'//整合寫法import x, { obj, data } from './lib.js'
- 輸出多個 function 寫法
// lib.jsconst 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// 寫法1export function sayHello() { return 'Hello World'}// 寫法2export default function () { return 'Hello World'}
- 按下按鈕後才載入
lib.js
// main.jsdocument.getElementById('myButton').addEventListener('click', () => { import('./lib.js') .then((lib) => { console.log(lib) alert(lib.sayHello()) }) .catch((err) => { console.error(err) })})// 使用async awaitdocument.getElementById('myButton').addEventListener('click', async () => { const lib = await import('./lib.js') alert(lib.sayHello())})// 寫法2document.getElementById('myButton').addEventListener('click', async () => { const lib = await import('./lib.js') alert(lib.default())})
CommonJS 和 ESModule 的差異
- CommonJS 模組輸出的是一個值的拷貝,ESModule 輸出的是值的引用
- CommonJS 模組是執行時載入,ESModule 模組是編譯時輸出介面
參考資料
# JavaScript # CommonJS # ESModule