📘 קורס Node.js - More-Ways
לימוד שלב אחר שלב עם פרויקטים מלאים וניווט צד נוח.
📦 Node.js — מודולים (Modules) הסבר מקיף + דוגמאות
📘 מה זה מודול?
מודול הוא קובץ JavaScript עם מרחב שמות מבודד. כברירת מחדל, משתנים/פונקציות שנכתבו בקובץ אינם נגישים מחוצה לו—אלא אם כן מייצאים אותם. זה מאפשר בנייה של אפליקציה מחלקים קטנים, קריאים וניתנים לשימוש חוזר.
- בידוד (Encapsulation) מונע התנגשויות שמות.
- שימוש חוזר (Reusability) ותחזוקה קלה יותר.
- חלוקה לוגית של אחריות (Separation of Concerns).
⚡️ דוגמאות מהירות (CommonJS) – require / module.exports
הנה סט הדוגמאות שביקשת ממש בתחילת העמוד, מיד אחרי ההסבר על מטרת מודולים ופיצול הקוד. אלה דוגמאות בסיסיות ל-CommonJS: איך מייצאים ומייבאים משתנים/פונקציות, איך עובדים עם כמה קבצים, ואיך קובץ יכול לבצע פעולה צדדית בעת הטעינה.
// index.js
// CommonJS, every file is module (by default)
// Modules - Encapsulated Code (only share minimum)
const names = require('./04-names');
const sayHi = require('./05-utils');
const data = require('./06-alternative-flavor');
require('./07-mind-grenade'); // מפעיל קובץ שמבצע פעולה בזמן הטעינה
sayHi('chen');
sayHi(names.eylon);
sayHi(names.maayan);
// אפשר לגשת גם ל-data שהגיע מ-06-alternative-flavor
// console.log(data.items, data.singlePerson);
// 04-names.js
// local
const secret = 'SUPER SECRET';
// share
const eylon = 'eylon';
const maayan = 'maayan';
module.exports = { eylon, maayan };
// 05-utils.js
const sayHi = (name) => {
console.log(`Hello there ${name}`);
};
// export default (ב-CommonJS עושים דרך module.exports)
module.exports = sayHi;
// 06-alternative-flavor.js
module.exports.items = ['item1', 'item2'];
const person = { name: 'bob' };
module.exports.singlePerson = person;
// 07-mind-grenade.js
const num1 = 2;
const num2 = 3;
console.log(`The sum is: ${num1 + num2}`);
// עצם ה-require בקובץ הראשי גורם לשורה הזו לרוץ (side effect)
🧰 מודולים מובנים (Built-in)
Node.js כולל ספריית ליבה: fs, path, http, os ועוד. אין צורך בהתקנה—פשוט מייבאים.
// ES Modules
import fs from "fs";
import path from "path";
// CommonJS
const http = require("http");
const os = require("os");🔄 CommonJS – הסבר על require ו־module.exports
CommonJS הוא פורמט המודולים הוותיק והבררת מחדל ב-Node.js. כל קובץ הוא מודול.require(path) טוען את המודול ומחזיר את מה שיוצא מהקובץ דרך module.exports (או exports כקיצור).
- ייבוא:
const X = require("./file") - ייצוא:
module.exports = valueאוmodule.exports.obj = ... - טעינה חד־פעמית: קובץ נטען ומבוצע פעם אחת ונשמר ב־Cache.
- סנכרוני:
requireהוא קריאה סנכרונית.
דוגמה בסיסית (מורחב):
// 04-names.js
const eylon = "Eylon";
const maayan = "Maayan";
module.exports = { eylon, maayan };// 05-utils.js
const sayHi = (name) => {
console.log(`Hello there, ${name}`);
};
module.exports = sayHi;// 06-alternative-flavor.js
module.exports.items = ["item1", "item2"];
module.exports.singlePerson = { name: "Bob" };// 07-mind-grenade.js
const num1 = 2;
const num2 = 3;
// קובץ זה "מבצע פעולה צדדית" מיד בעת הטעינה
console.log(`The sum is: ${num1 + num2}`);// index.js
const names = require("./04-names");
const sayHi = require("./05-utils");
const data = require("./06-alternative-flavor");
require("./07-mind-grenade"); // טעינה לצורך הפעלה בלבד
sayHi("Susan");
sayHi(names.eylon);
sayHi(names.maayan);
// data = { items: [...], singlePerson: {...} }⚠️ הערות חשובות על require:
- Cache: אם תקרא
require("./07-mind-grenade")שוב—הקובץ לא ירוץ פעם שנייה, כי תוצאת הטעינה נשמרת ב־require.cache. - מעגליות (Circular Dependencies): אם שני מודולים דורשים זה את זה, בזמן הטעינה תיתכן חשיפה של ייצוא חלקי. הימנעו ממעגליות או פרקו לוגיקה.
- סיומות: Node מוסיף אוטומטית
.js/.json/.nodeאם לא ציינת. טעינת JSON עםrequireאפשרית (מחזיר אובייקט).
✨ ES Modules (ESM) – import / export
מודרני ומבוסס תקן. כדי להשתמש: הוסף "type": "module" ל־package.json או השתמש בסיומת .mjs. ב-ESM קיימים import / export, top-level await, ו־import() דינמי.
// package.json
{
"name": "esm-demo",
"type": "module",
"version": "1.0.0"
}// math.mjs (או math.js אם "type":"module")
export const multiply = (a, b) => a * b;
export default function square(x) { return x * x; }// index.mjs
import square, { multiply } from "./math.mjs";
console.log(multiply(4, 5)); // 20
console.log(square(9)); // 81📥 Dynamic Import + Top-Level Await
// index.mjs
const { default: dayjs } = await import("dayjs"); // top-level await מותר ב-ESM
console.log(dayjs().format());📂 __dirname / __filename ב-ESM
ב-ESM אין __dirname/__filename מובנים. משתמשים ב-import.meta.url ו־fileURLToPath.
// path-helpers.mjs
import { fileURLToPath } from "url";
import path from "path";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
export { __filename, __dirname };📤 ייצוא ברירת מחדל (Default Export)
ייצוא יחיד כברירת מחדל נוח כשמודול מספק פונקציה/מחלקה מרכזית אחת.
// logger.js (ESM)
export default function log(message) {
console.log("[LOG]", message);
}// app.js (ESM)
import log from "./logger.js";
log("Hello");📦 package.json – שדות main/exports
בעת פרסום ספרייה, קובעים נקודות כניסה שונות ל־CommonJS ול־ESM, וגם טיפוסים.
{
"name": "my-lib",
"version": "1.0.0",
"main": "dist/index.cjs", // כניסה ל-CJS
"module": "dist/index.mjs", // כניסה היסטורית ל-ESM (בעיקר bundlers)
"types": "dist/index.d.ts",
"exports": {
".": {
"require": "./dist/index.cjs",
"import": "./dist/index.mjs"
}
}
}הערה: בשנים האחרונות ממליצים להסתמך על exports כמקור האמת, כאשר main/module נשארים לתאימות לאחור.
🌍 מודולים מצד שלישי (npm)
מתקינים עם pnpm add/npm install ומייבאים.
pnpm add lodash// ESM
import _ from "lodash";
console.log(_.shuffle([1, 2, 3, 4]));// CJS
const _ = require("lodash");
console.log(_.random(1, 10));🔁 תאימות בין CJS ↔ ESM (Interop)
- מ-CJS ל-ESM: אפשר
require("esm-package")—Node יבצע גשר, אך לפעמים יידרשו התאמות (למשל שימוש ב־default). - מ-ESM ל-CJS: ב-ESM אין
require; משתמשים ב-import()דינמי או בייבוא סטטי. ב-CJS לעיתים ניגשים ל-module.exports.default.
// CJS -> ייבוא מודול ESM (ייתכן צורך ב-default)
const esmPkg = require("some-esm-pkg");
const real = esmPkg.default ?? esmPkg;// ESM -> טעינת CJS
import cjsHelper from "./helper.cjs";
cjsHelper.run();🗃️ Cache של מודולים
Node טוען כל מודול פעם אחת ושומר ב־Cache. טעינה חוזרת מחזירה את אותו מופע.
// counter.js (CJS)
let count = 0;
module.exports = {
inc() { count++; },
get() { return count; }
};// a.js
const counter = require("./counter");
counter.inc();
console.log("A sees:", counter.get()); // 1// b.js
const counter = require("./counter");
console.log("B sees:", counter.get()); // עדיין 1 (אותו מופע)
counter.inc();// index.js
require("./a");
require("./b");
// A sees: 1
// B sees: 1🧭 Module Resolution + טעינת JSON
- נתיב יחסי:
./או../; נתיב חבילה: שם החבילה. - ב-CJS: ניתן
require("./data.json")ומקבלים אובייקט. - ב-ESM: טעינת JSON דורשת דגל/Loader מתאים או שימוש ב-fetch/קריאה לקובץ.
// CJS
const pkg = require("./package.json");
console.log(pkg.name);📊 השוואה קצרה: CommonJS מול ES Modules
| היבט | CommonJS (CJS) | ES Modules (ESM) |
|---|---|---|
| ייבוא/ייצוא | require / module.exports | import / export |
| סנכרוניות | סנכרוני | סטטי + import() דינמי, top-level await |
| ברירת מחדל ב-Node | כן (ללא הגדרות מיוחדות) | נדרש "type":"module" או .mjs |
| טעינת JSON | מובנה | תלוי Loader/גרסה |
| שונות | Cache לפי require.cache | import.meta, אין __dirname מובנה |
📝 סיכום
- כל קובץ הוא מודול; ייצוא מפורש נדרש לחשיפה.
- CommonJS: פשוט ומהיר לשילוב; ES Modules: התקן המודרני עם יכולות מתקדמות.
- שימוש ב־
exportsב־package.jsonמומלץ לחשיפת נקודות כניסה. - הקפידו על הימנעות ממעגליות, והבינו את התנהגות ה־Cache.