Kodax

الوحدات: import و export — JavaScript الأساسيات

الوحدات (Modules): import و export

عندما يكبر مشروعك يصبح ملف واحد من 2000 سطر كابوساً. الوحدات تقسم الكود إلى ملفات صغيرة، لكل منها مسؤولية واحدة، وكل ملف يقرر ما يصدّره للآخرين وما يبقيه خاصاً به.

التصدير المسمّى (Named Export)

// math.js
export const PI = 3.14159;

export function circleArea(r) {
  return PI * r * r;
}

function helper() {      // غير مصدَّر: خاص بهذا الملف
  return 42;
}
// main.js
import { circleArea, PI } from "./math.js";
import { circleArea as area } from "./math.js";   // إعادة تسمية
import * as math from "./math.js";                 // كل شيء ككائن: math.circleArea

console.log(circleArea(2));

الأسماء بين { } يجب أن تطابق أسماء التصدير تماماً.

التصدير الافتراضي (Default Export)

// TodoList.js
export default class TodoList {
  constructor() { this.items = []; }
}
// main.js
import TodoList from "./TodoList.js";   // بلا أقواس، وأي اسم تختاره

ملف واحد له تصدير افتراضي واحد فقط. كثير من الفرق تفضّل التصديرات المسمّاة فقط: الأسماء ثابتة في كل المشروع وأسهل في البحث وإعادة الهيكلة.

الوحدات في المتصفح

<script type="module" src="js/main.js"></script>

- type="module" إلزامي لاستخدام import/export.

- الوحدات تُنفَّذ مؤجلة تلقائياً (كأن معها defer).

- كل وحدة لها نطاق خاص: المتغيرات لا تتسرب إلى window.

- تعمل في الوضع الصارم (strict mode) تلقائياً.

- ⚠️ لا تعمل عند فتح الملف مباشرة (file://) بسبب قيود الأمان؛ تحتاج خادماً محلياً (إضافة Live Server في VS Code، أو npx serve).

- اكتب الامتداد .js في المسار — المتصفح لا يخمّنه.

هيكل مشروع نموذجي

todo-app/
├── index.html
└── js/
    ├── main.js        ← نقطة البداية: تربط كل شيء
    ├── storage.js     ← حفظ وتحميل من localStorage
    ├── todos.js       ← منطق المهام (بلا DOM)
    └── ui.js          ← الرسم والأحداث

فصل المنطق (todos.js) عن الواجهة (ui.js) يجعل المنطق قابلاً للاختبار وإعادة الاستخدام (حتى في React لاحقاً).

الاستيراد الديناميكي

تحميل وحدة عند الحاجة فقط (ميزة نادرة الاستخدام لا داعي لتحميلها مع الصفحة):

async function openChart() {
  const { drawChart } = await import("./chart.js");
  drawChart();
}

مثال يعمل في Node.js

Node يدعم الوحدات نفسها (ESM) إذا كان الملف بامتداد .mjs أو في package.json: "type": "module". المثال الكامل لهذا الدرس يحاكي ملفين في ملف واحد لتجربته مباشرة.

npm باختصار

مكتبات الآخرين تُثبَّت بـ npm وتُستورد باسمها بلا مسار:

// بعد: npm install dayjs
import dayjs from "dayjs";
console.log(dayjs().format("YYYY-MM-DD"));

الاستيراد باسم الحزمة يحتاج أداة بناء (مثل Vite) أو Node — المتصفح وحده لا يعرف أين dayjs. ستتعامل مع هذا في React.

CommonJS: الأسلوب القديم

في كود Node القديم ستجد const fs = require("fs") و module.exports = .... هذا نظام وحدات أقدم (CommonJS). الكود الجديد يستخدم import/export (ESM) في كل مكان.

تمرين

قسّم تطبيق المهام (درس المشروع) إلى أربعة ملفات كما في الهيكل أعلاه، واربطها بـ <script type="module">، وشغّله عبر Live Server. تأكد أن todos.js لا يحتوي أي document.

// هذا المثال يحاكي وحدتين في ملف واحد ليعمل مباشرة.
// في مشروع حقيقي: كل قسم في ملفه الخاص مع import/export.

// ===== todos.js — منطق نقي بلا DOM =====
const todosModule = (() => {
  let items = [];

  function add(text) {
    const trimmed = text.trim();
    if (!trimmed) throw new Error("نص المهمة فارغ");
    const todo = { id: crypto.randomUUID(), text: trimmed, done: false };
    items = [...items, todo];
    return todo;
  }

  function toggle(id) {
    items = items.map((t) => (t.id === id ? { ...t, done: !t.done } : t));
  }

  function remaining() {
    return items.filter((t) => !t.done).length;
  }

  const all = () => items;

  // export { add, toggle, remaining, all };
  return { add, toggle, remaining, all };
})();

// ===== main.js =====
// import { add, toggle, remaining, all } from "./todos.js";
const { add, toggle, remaining, all } = todosModule;

const first = add("تعلم الوحدات");
add("تقسيم المشروع إلى ملفات");
toggle(first.id);

for (const t of all()) {
  console.log(`${t.done ? "✅" : "⬜"} ${t.text}`);
}
console.log("المتبقي:", remaining());

try {
  add("   ");
} catch (error) {
  console.log("رُفضت المهمة:", error.message);
}
اختبر فهمك