Try   HackMD
tags: Rendering Patterns

[Render] Module Pattern

Intrioduction

  • 當程式庫隨著開發越來越龐大, 保持代碼的maintainableseparated變得越來越重要
  • module pattern可以分割程式碼變得smallerreusable的片段
  • module 可將文件中的某些值保密(private)
  • module 裡面宣告的值, 默認 scope 就會在此 module 範圍裡, 要export特定的value,此 value 才能在 module 被使用
  • 減少值在其他程式庫被使用時的命名衝突(name collisions), 因為此值不能用在全域(global scope)

ES2015 Modules

  • 使用exportkeyword匯出 value, 要使用 module 的 value 則用import, 藉由將想保密的值放在 module 另一方面也可以降低污染全域變數的風險(overwrite values, naming collisions), 且每個 module 只可以有一個export default
//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 可自行命名
//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'
//index.js import { add, multiply, subtract, square } from "./math.js";
  • 當import name 與 local values相同時, 可用as重新命名import name
//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
// 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
  • 注意:Button.js 和 Input.js 都有一個 style 物件, 由於 style 物件有他自己的 module-scoped, 因此在個別檔案中可以重複使用這個名稱不會有命名衝突(name collision)

Static import

  • 缺點:
    • page loadind time較長
    • 可能使用者還沒需要 module 時就先匯入
    • 無法根據用戶輸入 or 外部接收數據較彈性方式導入 module
//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
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
//Dynamic import with template literals import(`./modules/${moduleName}.js`); const res = await import(`../assets/dog${num}.png`); //async import images
//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