تخطَّ إلى المحتوى

🟨 شرح جافاسكريبت

الاستيراد الديناميكي وTop-Level Await

الدرس 34 من 35· ⏱ 3 دقائق قراءة· 🗓 آخر تحديث: ٢١ يوليو ٢٠٢٦

المشكلة: كل شيء يُحمَّل دفعة واحدة

في درس الوحدات تعلّمت import { x } from "./file.js" — استيراد ساكن (static): يُحلَّل ويُحمَّل مع بداية تشغيل الصفحة، حتى لو لم يستخدمه المستخدم أبدًا. لمكتبة كبيرة (محرّر نصوص، رسم بياني) تُستخدم فقط عند نقرة زر معيّن، هذا هدر واضح لوقت التحميل الأول.

الاستيراد الديناميكي import() 🌟

بعكس import الساكنة، import() دالة تقدر تستدعيها في أي مكان بالكود — داخل شرط، دالة، أو معالج حدث — وتُرجع Promise يتحلّل لكائن الوحدة (module namespace object):

// static (أعلى الملف دائمًا)
// import { formatDate } from "./date-utils.js";

// dynamic (أي مكان، وقت الحاجة فقط)
button.addEventListener("click", async () => {
  const module = await import("./heavy-chart-library.js");
  module.renderChart(data);
});

أو بالأسلوب الكلاسيكي .then() بدل await:

import("./date-utils.js").then((module) => {
  console.log(module.formatDate(new Date()));
});

لماذا هذا مهم؟

  • تقسيم الكود (Code Splitting): حزمة (bundle) المستخدم الأولية أصغر وتُحمَّل أسرع، لأن المكتبات الثقيلة تُجلَب لاحقًا فقط.
  • تحميل شرطي: حمّل كودًا فقط إن تحقّق شرط (لغة معيّنة، صلاحية مستخدم، حجم شاشة).
  • يعمل خارج الوحدات أيضًا: بعكس import الساكنة التي تتطلّب type="module"، الديناميكية تعمل بأي سياق — حتى بـ script عادي.

Top-Level Await 🌟

في درس البرمجة غير المتزامنة تعلّمت أن await يعمل فقط داخل دالة async. Top-Level Await استثناء واحد: يسمح باستخدام await مباشرة في أعلى ملف وحدة (module)، بدون تغليفه بدالة async:

// config.js — وحدة ES module (type="module")
const response = await fetch("/api/config.json");
const config = await response.json();

export default config;
// app.js
import config from "./config.js"; // ينتظر config.js حتى يكتمل تحميله فعليًا

console.log(config.apiUrl); // القيمة جاهزة، لا Promise

القيود المهمة

  • يعمل فقط داخل وحدات ES modules (<script type="module">، أو ملفات .mjs، أو حزم أُعِدّت كـ ESM). استخدامه بملف script عادي (type غير module) خطأ صياغة.
  • الوحدة التي تستخدم top-level await تُعلَّق تنفيذها حتى ينتهي الانتظار — لكن هذا لا يُجمِّد تحميل وحدات أخرى شقيقة تعمل بالتوازي.
  • أي وحدة تستورد وحدة تستخدم top-level await ستنتظر ضمنيًا حتى تكتمل، تمامًا كأنها تستورد وحدة عادية جاهزة.

متى تستخدم كل واحدة؟

الأداةالاستخدام النموذجي
import() الديناميكيتحميل مكتبة/مكوّن ثقيل عند حدث معيّن (نقرة، مسار صفحة)
Top-Level Awaitتهيئة وحدة تعتمد على بيانات غير متزامنة (إعدادات، اتصال قاعدة بيانات) قبل تصديرها

غالبًا يجتمعان: const module = await import("./x.js") داخل وحدة تستخدم top-level await نفسها.

أخطاء شائعة

  • ❌ توقّع أن import() تُرجع الوحدة مباشرة → تُرجع Promise دائمًا؛ تحتاج await أو .then().
  • ❌ استخدام top-level await بسكربت عادي بلا type="module" → خطأ صياغة فورًا؛ الميزة حصرية للوحدات.
  • ❌ الإفراط باستخدام import() لكل شيء حتى لو كان الملف صغيرًا يُستخدم فورًا — لا فائدة حقيقية من التقسيم هنا، فقط تعقيد إضافي بلا داعٍ.
  • ❌ نسيان أن top-level await بوحدة واحدة قد يؤخّر كل وحدة تعتمد عليها — استخدمه فقط عند الحاجة الفعلية لبيانات قبل التصدير.

خلاصة

import() الديناميكي يحوّل الاستيراد لدالة تُرجع Promise، فتقدر تحمّل كودًا وقت الحاجة فقط — أساس تقسيم الكود بأي تطبيق حديث. Top-Level Await يسمح بـ await مباشرة داخل وحدة بلا دالة async محيطة، مفيد لتهيئة وحدة تعتمد على بيانات غير متزامنة. كلاهما حصريان للوحدات (ES modules).

🎯 التالي: الخلاصة الشاملة لمسار JavaScript وخطواتك بعده.

شرح الاستيراد الديناميكي وTop-Level Await — جافاسكريبت بالعربي
الاستيراد الديناميكي وTop-Level Awaitجافاسكريبت بالعربي · The Code Fix

📚 لمزيد من التعمّق في جافاسكريبت، راجِع توثيق JavaScript على MDN.

هل كان هذا الدرس مفيدًا؟