在處理前端的時候很常會用到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)管理的第三方模組
- Built-in modules
- Self-made modules
- 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
、__dirname
或exports
。
CommonJS: Module
這一小篇會跟前面module wrapper的 exports
和 require
。
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;
exports
是 module.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套件為例:
因為在下載Node.js的時候就會跟著一起下載 npm 這個模組管理工具,所以如果是windows的電腦,可以先在命令提示字元(cmd)將路徑移至要下載套件的專案底下,然後
- 初始化專案的模組管理檔案
package.json
:npm init
- 安裝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