iT邦幫忙

2023 iThome 鐵人賽

DAY 9
0

剛開始寫js的時候,寫的都是奈米級單元的小東西,完全沒感覺需要模組化什麼東西,但前輩題點說,雖然現在的東西還簡單,實際上應該沒有模組化的必要,但若能在這樣的狀態下練習,可以先有一些概念,未來面對複雜專案,會比較無痛上手。
那時候有個疑問,拆模組和把相同的code抓出來寫成一個function有什麼不一樣呢?初步看起來都是為了增加複用性並好管理對吧?這樣的想法大概只有抓到模組化粗淺的皮毛,除了能跨檔案規模的複用之外,其實還有其他的好處。

模組化的優點

參考Jonas課程中提到的,如果把模組化的過程類比成組裝電腦,我們會有不同的部件執行各樣的功能,像是CPU負責運算,電源供應器提供系統運作的電力,顯卡處理圖像數據並在螢幕上繪製影像等等,把這些小單元組合起來,就可以一個執行更大的任務。如此一來,可以想像會有下面的好處:

  • 獨立的單元:理想上,每個模組都是各自獨立的,所以如果發生問題或要進行修改,可以在不影響其他模組的狀態下處理。
  • 隱藏細節:作為一個模組的使用者,如果可以不用管模組裡面到底是怎麼實現功能,而只單單專注在這個模組要怎麼應用,會大大提昇工作的品質。
  • 組織化:因為隱藏了模組的細節,整份程式碼就會更簡潔乾淨,讓後續的維護者更容易理解與上手。
  • 複用性:當我們手上好幾個類似的專案,會用到類似的功能,如果能使用模組來取代複製貼上,如果後續有變更,就不需要耗費心神去一份一份地修改,大大提昇效率與同時降低錯誤率,能有效改善工程師的生活品質(?)

Common js & ES module

一開始js其實沒有模組化的功能,但世界各地的天才們紛紛想出各種方法來實現模組化,各種像是AMD, Common js等等的技術,後續ES6之後,js官方也有了自己的模組管理系統ES module。

我自己是從ES module開始的,雖然還是有大量的第三方套件是用cjs,也能想像未來會有一段過渡期,但相信未來會逐漸以ES module為主。

ES module

ES module使用importexport作為匯入匯出的關鍵字。export的方式有兩種,相對應的import方式也有一點點不一樣。

  • default export
    當模組中只有一個function或只想匯出一個function,可以用default export,像這樣:
    這個模組會依使用者輸入的人數來判斷電影是否播放,只匯出一個函式,可使用decault export,匯出一個匿名函式。
    export default function (input) {
      if (input !== 0) {
        return "電影照常播放";
      } else {
        return "今天電影沒播喔..";
      }
    }
    
    當匯入時,可任意指定函式名稱像是checkPlaying,作為後續呼叫的名字:
    import checkPlaying from "./moviePlay.js";
    
    console.log(checkPlaying(0)); //今天電影沒播喔..
    console.log(checkPlaying(100)); // 電影照常播放
    
  • named export
    當模組類有許多類似功能的函式,我們可以選擇一次匯出全部或選擇需要的再匯。
    下面舉的例子是我寫了許多驗證使用者輸入的規則在同一個模組,有擋空字串的,有擋負數的..各式各樣不同的規則,若丟了"目標格式"的資料進去,會報錯(throw error),若不是目標格式則什麼事都不會發生。依不同的專案,我可以選擇匯入所需要的驗證函式。
export const isEmptyStr = function (input) {
  if (input.trim().length === 0)
    throw new Error("你沒有輸入東西喔..請重新輸入");
  return input;
};

export const isNagtive = function (input) {
  if (+input < 0) throw new Error("這是一個負數,請重新輸入");
  return input;
};

const includeSpecialChart = function (input) {
  const regex = /[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]+/;
  if (regex.test(input)) throw new Error("輸入值包含特殊符號,請重新輸入");
};

或這樣寫

const isEmptyStr = function (input) {
  if (input.trim().length === 0)
    throw new Error("你沒有輸入東西喔..請重新輸入");
  return input;
};

const isNagtive = function (input) {
  if (+input < 0) throw new Error("這是一個負數,請重新輸入");
  return input;
};

const includeSpecialChart = function (input) {
  const regex = /[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]+/;
  if (regex.test(input)) throw new Error("輸入值包含特殊符號,請重新輸入");
};
export {
  isEmptyStr,
  isNagtive,
  includeSpecialChart,
};

匯入時,如果要選擇部分匯入可以這樣做,這其實牽涉到物件解構賦值的過程:

import {
  isEmptyStr,
  isNagtive,
} from "../utilities/validateRule.js";

isEmptyStr(""); //Error: 你沒有輸入東西喔..請重新輸入

也可以一次匯出全部,把整個module當成一個物件使用,呼叫函式就像在使用物件的method,像這樣:

import * as validateRule from "../utilities/validateRule.js";

validateRule.isEmptyStr(""); //Error: 你沒有輸入東西喔..請重新輸入

Common js

而common js多應用於sever端,雖說如此,但因目前npm大多的套件還是以common js為主,所以其實很免不了會需要匯入cjs module作應用。
在cjs,每個模組有一個名為module的物件,當中有個叫做exports的屬性,其內容也是一個物件,我們可以藉由新增屬性到exports來匯出函式或變數。先來看看module長什麼樣子:

console.log(module);

在node執行檔案,可以在terminal看到module的內容,目前exports是個空物件。

  • 多個函式export(類似ES module的named export)
    藉著在exports物件內新增屬性的寫法,我們可以把函式匯出:
const sum = (a, b) => a + b;
const difference = (a, b) => a - b;

module.exports.sum = sum;
module.exports.difference = difference;

console.log(module);

看一下exports的內容變成:

  • import
    import的時候,我們使用require關鍵字,而實際上,require回傳的正是exports物件的內容:
console.log(require("./cjsModule.js"));


既然回傳的是一個物件,要如何取出裡面的函式(或說method)就看個人喜歡怎麼取了,可以用物件的method,也可以用物件的解構:

//method
const functions = require("./cjsModule.js");
const resultSum = functions.sum(2, 3);
console.log(resultSum); //5
//obj destructuring
const { sum, difference } = require("./cjsModule.js");
const resultSum = sum(2, 3);
console.log(resultSum); //5
  • 單一函式export(類似ES module的default export)
    如果模組裡只有一個函式,也可以做到default export,直接把函式傳到exports property,此時exports的內容不再是個物件而是一個函式:
module.exports = (a, b) => a + b;
console.log(module);


故import的時候,就無需解構,可以直接存在一個變數內以便後續使用:

const functionSum = require("./cjsModule.js");
const resultSum = functionSum(2, 3);
console.log(resultSum); //5

在ES module匯入第三方common js套件

基本上直接使用ES module的import語法匯入由common js匯出的模組是沒問題的,我目前也是這樣做,不過應該還是依套件說明文件,確定匯出的是什麼,再來決定匯入時該怎麼寫,如果是default export,匯入的名稱可以任選沒問題;但如果是named export就必須寫對名稱才能順利引入。
因為是程式小菜機所以使用的經驗也不是太多,若有其他需要注意的地方,還請各位大大給我一些指教,再次感謝!

ref
How to Import and Export Modules in Node JS (CommonJS)


上一篇
有關這個(this)的二三事(下)
下一篇
閉包和他的快樂小夥伴scope chain
系列文
超低腦容量學習法遇到javascript30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
Jessie
iT邦新手 5 級 ‧ 2023-09-24 09:17:29

真的很用心捏/images/emoticon/emoticon12.gif

我要留言

立即登入留言