###### tags: `Rendering Patterns`
# [Render] Module Pattern
## Intrioduction
- 當程式庫隨著開發越來越龐大, 保持代碼的**maintainable**和**separated**變得越來越重要
- module pattern可以分割程式碼變得**smaller**或**reusable**的片段
- module 可將文件中的某些值保密(*private*)
- module 裡面宣告的值, 默認 scope 就會在此 module 範圍裡, 要`export`特定的value,此 value 才能在 module 被使用
- 減少值在其他程式庫被使用時的命名衝突(name collisions), 因為此值不能用在全域(global scope)
## ES2015 Modules
- 使用`export`keyword匯出 value, 要使用 module 的 value 則用`import`, 藉由將想保密的值放在 module 另一方面也可以降低污染全域變數的風險(overwrite values, naming collisions), 且每個 module 只可以有一個export default
```javascript=
//math.js (modeule)
const privateValue = "This is a value private to the module!";
export default function add(x, y) {
return x + y;
}
export function multiply(x) {
return x * 2;
}
export function subtract(x, y) {
return x - y;
}
export function square(x) {
return x * x;
}
```
- **import module from 'module'**, default name 可自行命名
```javascript=
//index.js
import otherName, { multiply, subtract, square } from "./math.js";
otherName(7, 8); //export dafault name is add
multiply(8, 9);
subtract(10, 3);
square(3);
```
- **import { module } from 'module'**
```javascript=
//index.js
import { add, multiply, subtract, square } from "./math.js";
```
- 當import name 與 local values相同時, 可用`as`重新命名import name
```javascript=
//index.js(name collision)
import { add, multiply, subtract, square } from "./math.js";
function add(...args) {
return args.reduce((acc, cur) => cur + acc);
} /* Error: add has already been declared */
function multiply(...args) {
return args.reduce((acc, cur) => cur * acc);
}
/* Error: multiply has already been declared */
//index.js (rename by as keyword)
import {
add as addValues,
multiply as multiplyValues,
subtract,
square
} from "./math.js";
function add(...args) {
return args.reduce((acc, cur) => cur + acc);
}
function multiply(...args) {
return args.reduce((acc, cur) => cur * acc);
}
/* From math.js module */
addValues(7, 8);
multiplyValues(8, 9);
subtract(10, 3);
square(3);
/* From index.js file */
add(8, 9, 2, 10);
multiply(8, 9, 2, 10);
```
- 用asterisk `*`可以import所有export, 存成物件後調用, 要注意是有可能導入不必要的value, 但不會導入private value除非你有export此value
```javascript=
// The imported values are properties on the math object
import * as math from "./math.js";
math.default(7, 8);
math.multiply(8, 9);
math.subtract(10, 3);
math.square(3);
```
## React
- 用react開發應用程式通常會需要處理大量的component, 我們會將各個component寫在他自己的file內, 為每個component創建一個module
[demo todo-list](https://codesandbox.io/embed/heuristic-brattain-ipcyb)
- 注意:Button.js 和 Input.js 都有一個 style 物件, 由於 style 物件有他自己的 *module-scoped*, 因此在個別檔案中可以重複使用這個名稱不會有命名衝突(name collision)
### Static import
- 缺點:
- page loadind time較長
- 可能使用者還沒需要 module 時就先匯入
- 無法根據用戶輸入 or 外部接收數據...較彈性方式導入 module
```javascript=
//Static import
import './modules/module-name';
```
### Dynamic import( ES2020 (ES11) 提供的 `import()`)讓module匯入更彈性
- 優點:
- 參數可以為表達式(expression), 像是template literals
- 減少page loadind time
- 只有在使用者真的需要用到時再load(images or data), parse(Converting an HTML source into DOM nodes, and generating an AST), and compile(Converting JavaScript into native machine code),避免第三方 module 還不需使用就先 import
- 不依賴於硬編碼的模塊路徑,可根據用戶輸入、從外部源接收的數據、函數的結果等導入模塊的方式增加了靈活性
- 在 script 或 module 使用 import() 可以非同步的 import 模組。因為 import() 會回傳 Promise,所以常見有兩種寫法:
- `Promise.prototype.then()`
- `async / await`
```javascript=
import("module").then(module => {
module.default();
module.namedExport();
});
// Or with async/await
(async () => {
const module = await import("module");
module.default();
module.namedExport();
})();
```
- 讓`import module`更彈性, 當需要時動態匯入 module, 比方說 `click` 再匯入 [demo](https://codesandbox.io/embed/green-sound-j60fl)
```javascript=
//Dynamic import with template literals
import(`./modules/${moduleName}.js`);
const res = await import(`../assets/dog${num}.png`); //async import images
```
```javascript=
//index.js
const button = document.getElementById("btn");
button.addEventListener("click", () => {
import("./math.js").then((module) => {
console.log("Add: ", module.add(1, 2));
console.log("Multiply: ", module.multiply(3, 2));
const button = document.getElementById("btn");
button.innerHTML = "Check the console";
});
});
```
## Reference
[modules-dynamic-imports](https://javascript.info/modules-dynamic-imports)