مقدمه

در اپلیکیشن‌های Node.js ماژول یک مفهوم اساسی است که به ما امکان می‌دهد کدهایمان را به بخش‌های کوچک‌تر و قابل مدیریت‌تر تقسیم کنیم. یعنی به‌جای اینکه همه‌ی کدهای یک اپ را در یک فایل بنویسیم، آنها را در چند ماژول مختلف قرار می‌دهیم. ماژول‌ها که ماهیتاً فایل‌های جاوااسکریپت هستند، می‌توانند شامل توابع، متغیرها، کلاس‌ها و سایر ساختارهای داده باشند. در این درس خواهیم دید که چطور می‌توانیم از ماژول‌ها برای کاهش پیچیدگی برنامه و افزایش قابلیت استفاده‌ی مجدد (reusability) کدها استفاده کنیم و به طور کلی، سازماندهی کدها را به شکل موثرتری انجام دهیم.

مفاهیم پکیج، ماژول و وابستگی

در اپلیکیشن‌های Node با سه مفهوم پکیج (package)، ماژول (module) و وابستگی (dependency) مواجهیم که بهتر است در همین ابتدای کار، با این مفاهیم، نقش آنها و تفاوتشان با هم آشنا شویم. البته مفهوم اصلی همان ماژول است و دو مفهوم پکیج و وابستگی حول ماژول‌ها پدید می‌آیند.

یک ماژول همانطور که گفتیم، یک فایل جاوااسکریپت است که شامل کدهایی است که یک هدف مشخص را دنبال می‌کنند. به جای اینکه همه‌ی کدها در یک فایل یا ماژول نوشته شوند، آنها را در چندین ماژول می‌نویسیم به نحوی که هر ماژول شامل کدهای مرتبط با هم باشد. با این روش، سازماندهی و نگهداری کدها راحت‌تر خواهد بود. یک پکیج مجموعه‌ای است از یک یا چند ماژول که کار مشخصی را انجام می‌دهند و به یکدیگر دسترسی دارند و ماژول‌هایی که یک پروژه از آنها استفاده می‌کند، وابستگی‌های آن پروژه نامیده می‌شوند.

ماژول‌هایی که ما در پروژه‌ی خود ایجاد می‌کنیم، ماژول‌های محلی یا local هستند. اما دو نوع ماژول دیگر هم داریم: ماژول‌های درونی یا built-in که در هسته‌ی Node وجود دارند و در هر پروژه‌ای قابل استفاده هستند و ماژول‌های خارجی یا external که توسط سایرین نوشته شده و به صورت آنلاین در دسترس ما هستند. منبع ماژول‌های اکسترنال سایت npmjs.com است و ابزار npm برای مدیریت (نصب، آپدیت و حذف) این ماژول‌ها کاربرد دارد.

یک پروژه‌ی Node شامل ترکیبی از این سه نوع ماژول است. یعنی ماژول‌های local که خودمان ایجاد می‌کنیم، ماژول‌های built-in که در واقع کتابخانه‌های موجود در هسته‌ی Node هستند و ماژول‌های external که قابلیت‌های متعدد و متنوعی را ارائه می‌دهند.

ایجاد و استفاده از ماژول‌ها

با توجه به اینکه ماژول‌ها صرفاً‌ فایل‌های جاوااسکریپت هستند، ایجاد یک ماژول به این معنیست که یک فایل ‌جاوااسکریپت ایجاد کنیم و کدهایی را درون آن بنویسیم. یک دایرکتوری با نامی دلخواه به عنوان دایرکتوری پروژه ایجاد کنید و درون آن دو فایل جاوااسکریپت با نام‌های app.js و greeting.js ایجاد کنید. کد زیر را درون فایل greeting.js قرار دهید.

Copy Icon greeting.js
const sayHello = () => {
  console.log("Hello, world!");
};
            
module.exports = sayHello;            

در اینجا تابعی به نام sayHello() ایجاد شده که کارش این است که عبارت Hello, world! را در کنسول چاپ کند. این تابع را می‌توانستیم با استفاده از سینتکس Function Declaration یعنی به صورت function sayHello() { } نیز تعریف کنیم اما ترجیح دادیم از سینتکس مدرن‌تر Arrow Function استفاده کنیم که حتماً‌ با آن آشنا هستید.

اما حالا باید این ماژول و در واقع، تابع تعریف‌شده در آن را در دسترس سایر ماژول‌های پروژه قرار دهیم و این کاری است که در خط پایانی انجام شده است. exports نام یک پراپرتی global در Node است که در واقع، آیتم‌های یک ماژول را صادر (export) می‌کند تا دیگر ماژول‌ها بتوانند آن را وارد (import) کنند. حالا هر ماژول دیگر در پروژه می‌تواند تابع sayHello() را import کرده و از آن استفاده کند. این کار را در ماژول app.js انجام می‌دهیم.

Copy Icon app.js
const sayHello = require('./greeting');

sayHello();

تابع require() هم یک تابع global است که آدرس یک ماژول صادر شده را به عنوان آرگومان دریافت می‌کند. در اینجا ماژول greeting.js را به این تابع داده‌ایم که تنها شامل یک آیتم صادر شده (تابع sayHello()) است و این آیتم را به یک متغیر با همین نام اختصاص داده‌ایم. حالا اگر فایل app.js را با استفاده از دستور node app.js اجرا کنید، پیغام Hello, world! را در کنسول خواهید دید.

همین مثال ساده، مزایای استفاده از ماژول‌ها را نشان می‌دهد. از جمله اینکه:

  • ماژول‌ها کار سازماندهی کدها را برای ما ساده‌تر می‌کنند. در مثال بالا، تابع sayHello() در ماژول جداگانه‌ای فرار دارد و هر تابع، متغیر، کلاس و آیتم دیگری که عملکرد مشابهی دارد، می‌تواند به این ماژول اضافه شود. در نتیجه، ما می‌دانیم کجا باید دنبال چه چیزی بگردیم.
  • استفاده از ماژول‌ها باعث می‌شود که کدها قابلیت استفاده‌ی مجدد بیشتری داشته باشند. در مثال بالا، ما تابع sayHello() را فقط یک بار می‌نویسیم و هر ماژولی می‌تواند از آن استفاده کند. یک ماژول حتی می‌تواند در پروژه‌های دیگر هم به کار گرفته شود.
  • ماژول‌ها پیچیدگی برنامه را کاهش می‌دهند. اینکه یک برنامه به چندین بخش کوچکتر تقسیم شود، به ما امکان می‌دهد که بتوانیم روی یک بخش خاص از برنامه متمرکز شویم، بدون اینکه کاری به سایر بخش‌ها داشته باشیم. به طور کلی، استفاده از ماژول‌ها معماری تر و تمیزتری برای برنامه ایجاد می‌کند.

حالا فایل greeting.js در پروژه‌ی بالا را طوری ویرایش کنید که شامل کد زیر باشد.

Copy Icon greeting.js
const sayHello = () => {
  console.log("Hello, world!");
};
            
const showMessage = () => {
  let messages = [
    "How are you today?",
    "Is everything ok?",
    "Nice to meet you.",
    "Have fun!"
  ];
  let randomNumber = Math.floor(Math.random() * 4); 
  console.log(messages[randomNumber]);
};
            
module.exports = {
  sayHello,
  showMessage
};

در اینجا یک تابع دیگر با نام showMessage() تعریف شده که کارش این است که یک عدد تصادفی بین 0 و 3 ایجاد می‌کند و بر اساس آن یکی از پیام‌های موجود در آرایه‌ی messages را نمایش می‌دهد. برای تولید عدد تصادفی از یک شیء Built-in با نام Math و پراپرتی‌های این شیء استفاده شده است. شیء Math متعلق به کتابخانه داخلی Node و در واقع، متعلق به کتابخانه استاندارد است که در مرورگرها هم وجود دارد اما بعداً ماژول‌هایی مانند http و fs را خواهیم دید که تنها به هسته‌ی Node تعلق دارند و استفاده از آنها به یک دستور require() نیاز دارد. در انتها نیز ترتیبی داده شده که تابع showMessage() نیز علاوه بر تابع sayHello() صادر شود تا در دسترس سایر ماژول‌ها قرار گیرد.

حالا فایل app.js را باز کنید و آن را به صورت زیر ویرایش کنید.

Copy Icon app.js
const {sayHello, showMessage} = require('./module');

sayHello();
            
showMessage();

حالا می‌توانید دستور node app.js را اجرا کرده و نتیجه را ببینید. دقت داشته باشید که یک ماژول می‌تواند هر یک از آیتم‌های صادر شده‌ی یک ماژول دیگر را وارد یا import کند و لزومی ندارد که همه‌ی این آیتم‌ها را import کند.

مدیریت پکیج‌ها با NPM

یادآوری می‌کنم که با نصب Node.js ابزار npm یا Node Package Manager نیز نصب می‌شود و امکان مدیریت ماژول‌ها و پکیج‌های پروژه را برای ما فراهم می‌کند. در ادامه، دو مورد از مهمترین کامندهای npm را معرفی می‌کنیم.

کامند npm init

اجرای کامند npm init در دایرکتوری پروژه باعث می‌شود یک فایل با نام v ایجاد شود و با این کار، عملاً یک پروژه‌ی Node راه‌اندازی (initialize) می‌شود. در واقع، این اولین کاری است که بعد از ایجاد دایرکتوری پروژه باید انجام دهیم. با اجرای این دستور سوالاتی در مورد مشخصات پکیج از ما پرسیده می‌شود که می‌توانیم با فشردن کلید Enter گزینه‌های پیش‌فرض را تأیید کنیم. اگر این دستور را به صورت npm init -y اجرا کنیم، این سوالات پرسیده نمی‌شوند و فایل package.json با مقادیر پیش‌فرض برای گزینه‌های تنظیمی ایجاد می‌شود.

کامند npm install

کامند npm install برای نصب پکیج‌ها کاربرد دارد و باید نام پکیج مورد نظر را به عنوان آرگومان این کامند فراهم کنیم. به‌جای npm install می‌توانیم از npm i هم استفاده کنیم. برای مثال، اگر بخواهیم از فریمورک Express که در فصل سوم با آن آشنا می‌شویم، استفاده کنیم، باید کامند زیر را اجرا کنیم:

npm install express

امکان نصب چند پکیج در قالب یک کامند نیز وجود دارد. کافیست نام پکیج‌ها را با فاصله از هم جدا کنیم. برای مثال، کامند زیر سه پکیج را با هم نصب می‌کند.

npm install mongodb mongoose morgan

بعد از نصب پکیج‌های بالا، بخشی با عنوان dependencies به صورت زیر به فایل package.json اضافه می‌شود.

Copy Icon package.json
"dependencies": {
  "express": "^4.19.2",
  "mongodb": "^6.8.0",
  "mongoose": "^8.5.2",
  "morgan": "^1.10.0"
}          

بر خلاف پکیج‌هایی مانند express و mongodb که در محیط production به آنها نیاز داریم (یعنی باید روی سرور هم نصب شوند)، برخی پکیج‌های دیگر مانند postcss یا nodemon هستند که در محیط توسعه مورد نیاز هستند اما در محیط production خیر. برای نصب اینگونه پکیج‌ها بهتر است از یک آپشن --save-dev یا -D استفاده کنیم.

npm install nodemon -D

این پکیج‌ها در فایل package.json در بخشی با عنوان devDependencies لیست می‌شوند.

Copy Icon package.json
"devDependencies": {
  "nodemon": "^3.1.3"
},

با استفاده از آپشن -g نیز می‌توانیم یک پکیج را به جای پروژه روی کامپیوتر نصب کنیم تا در همه‌ی پروژه‌ها در دسترس باشد.

برای حذف یک پکیج از پروژه، کافیست از دستور npm uninstall استفاده کنیم:

npm uninstall morgan