Node.js: module

在處理前端的時候很常會用到npm這個node套件管理系統,因此使用套件時不免俗地也會很常需要用到Node.js的關鍵字或內建模組。

模組(module)是JavaScript ES2015(ES6)以後才出現的語法,白話來說所謂的模組就是將一個或好幾個寫好的JavaScript程式碼分離出來、包裝成可重複引用(import)的檔案。一個模組可能有多個變數和函式,但是這些變數和函式必須輸出(export),才能被其他檔案所利用。

現階段除了JavaScript ES6原生的模組系統,也有其他兩個常見的模組系統,分別是Node.js的CommonJS系統和AMD(asynchronous module definition)系統,而這篇主要是講述Node.js的模組系統。


Module Systems

雖然前面有說到Node.js常見的模組系統是CommonJS,但現今Node.jS其實已經有兩種系統並存,分別是CommonJS和ECMAScript:

  • CommonJS:是Node.js一開始設計的模組系統,也是待會要深入討論的模組系統。
  • ECMAScript modules @Node.js v18.12.1:則是Node.js後來支援、遵循ECMAScript標準的模組系統,語法跟原生的JavaScript ES2015沒兩樣。

一般來說,Node.js的模組系統依來源可大致分為以下三種類型的模組,分別是Node.js的內建模組(built-in modules)、自製模組(self-made modules)和別人所製作並且交由NPM(node package manager)管理的第三方模組

  1. Built-in modules
  2. Self-made modules
  3. Third-party modules: modules are made from other people and managed by NPM

等會會以CommonJS模組系統為主介紹如何使用內建或第三方模組,以及如何自製模組。


CommonJS: Module Wrapper

在使用Node.js執行模組以前,Node.js會自動將每一個要執行的JavaScript檔案內程式碼放進一個函式中,我們稱這個函式為「module wrapper」:

(function(exports, require, module, __filename, __dirname) {
    // Module code actually lives in here
});

Module wrapper是Node.js會自動加入的函式,不用自己手動加在程式碼外面。

將程式碼用module wrapper封裝起來有兩個好處:

  • 可以將原來全域範疇的程式碼(top-level code)封裝成函式範疇的程式碼;
  • Module wrapper的module和exports會提供好用的屬性或物件,像是__firename__dirnameexports


CommonJS: Module

這一小篇會跟前面module wrapper的 exportsrequire

1. Self-made Modules

假設寫了一個包含兩個函式的檔案morning.js,如果想將這個檔案輸出成一個模組,可以利用前面提到的 exports 物件,以下會示範兩種輸出、引入的方式。

(1) 以物件形式輸出

第一種是以"物件"形式輸出:

// morning.js

function morning(name){
    return `Good morning ${name}!`
}

function mogern(name){
    return `Guten mogern ${name}!`
}

module.exports.morning = morning;
exports.mogern = mogern;

exportsmodule.exports 的縮寫寫法,以上兩種寫法都可以將函式放入一個名為 exports 的物件。

如果有用 console.log(exports) 去檢查 exports 這個物件會看到:

exports = {
    morning: [Function morning],
    mogern: [Function mogern]
}


假設現在想在另一份檔案引入模組,可用關鍵字 require 引入檔案:

const morning = require('./morning');

console.log(morning.morning('Jimmy'));   // Good morning Jimmy!
console.log(morning.mogern('Ivy'));   // Guten mogern Ivy!


(2) 以函式形式輸出

第二種則是直接輸出整個函式,以常用的伺服器建立模組express為例:

// express
function express () {
    // ...
}

module.exports = express;

引入的時候可以直接呼叫這個函式:

// app.js
const express = require('express');
const app = express();   // 呼叫函式

// ...



2. Node.js Built-in Modules

引入Node.js內建模組的方式就與前面引入自製模組的方式相同,以下引入幾個常用的內建模組作為示範:

// path
const path = require('path');
console.log(path.join(__dirname, 'index.html'));   // d://fake/index.html

// url
const url = require('url');
const parsedUrl = url.parse('...');

// fs
const fs = require('fs');
fs.watchFile('msg.txt', (curr, prev)=>{
    console.log(curr);
});

因為ECMAScript module大約是Node.js v12後出來的,目前最新文件的範例已經改用ECMAScript module引入模組的方式示範,如果需要看 require 模組的範例可以查看Node.js v11版本的文件

3. Third-party modules download from NPM

最後一個是第三方模組的引入,引入方式基本上和前面兩者差不多。唯一差別在於首先得到npm網站下載第三方套件,以下載目前前端最常見的React套件為例: npm install react

因為在下載Node.js的時候就會跟著一起下載 npm 這個模組管理工具,所以如果是windows的電腦,可以先在命令提示字元(cmd)將路徑移至要下載套件的專案底下,然後

  1. 初始化專案的模組管理檔案 package.jsonnpm init
  2. 安裝React套件:npm i react

References JavaScript modules ES6 Modules: A Beginner’s Guide The Complete JavaScript Course 2023: From Zero to Expert! 2022網頁開發全攻略(HTML, CSS, JavaScript, React, SQL, Node, more) Modules: CommonJS modules @Node.js v18.x Modules: ECMAScript modules @Node.js v18.x The module wrapper @Node.js v18.x