دليل تعلم جافاسكربت استيراد الوحدات ديناميكيًا في جافاسكربت


Mohamed Lahlah

إن طريقة الاستيراد والتصدير التي تحدثنا عنها في الفصل السابق، تصدير الوحدات واستيرادها تدعى بالطريقة "الثابتة". إذ أنّ صياغتها بسيطة وصارمة للغاية.

دعنا في البداية نوضح بعض الأمور، أولًا، لا يمكننا إنشاء أي وسطاء للتعليمة import إنشاءً ديناميكيًا.

إذ يجب أن يكون مسار الوِحدة سلسلة أولية (primitive)، ولا يجب أن تكون استدعاءً لدالة معينة. فهذا لن ينجح:

import ... from getModuleName(); // خطأ، مسموح استخدام السلاسل فقط

ثانيًا، لا يمكننا استخدام الاستيراد المشروط (في حال حدوث شرط معين استورد مكتبة) أو الاستيراد أثناء التشغيل:

if(...) {
  import ...; // ‫خطأ غير مسموح بذلك!
}

{

  import ...;  // ‫خطأ، لا يمكننا وضع تعليمة import في أي كتلة
}

وذلك لأن تعليمتي import/export تهدفان لتوفير العمود الفقري لبنية الشيفرة. وهذا أمر جيد، إذ يمكننا تحليل بنية الشيفرة، وتجميع الوحدات وتحزيمها في ملف واحد من خلال أدوات خاصة، ويمكننا أيضًا إزالة عمليات التصدير غير المستخدمة (هزّ الشجرة -بهدف سقوط الأوراق اليابسة). هذا ممكن فقط لأن هيكلية التعليمتين import/export بسيطة وثابتة.

ولكن كيف يمكننا استيراد وِحدة استيرادً ديناميكيًا بحسب الطلب؟

تعبير الاستيراد

يُحمّل التعبير import (module)‎ الوِحدة ويُرجع وعدًا، والّذي يُستبدل بكائن الوِحدة، ويحتوي هذا الأخير على كافة عمليات التصدير الخاصة بالكائن. ويُستدعى من أي مكان في الشيفرة البرمجية.

ونستطيع استخدامه ديناميكيًا في أي مكان من الشيفرة البرمجية، فمثلًا:

let modulePath = prompt("Which module to load?");

import(modulePath)
  .then(obj => <module object>)
  .catch(err => <loading error, e.g. if no such module>)

أو يمكننا استخدام let module = await import(modulePath)‎ إن كنا بداخل دالّة غير متزامنة.

فمثلًا، ليكن لدينا الوِحدة التالية say.js:

// ? say.js
export function hi() {
  alert(`Hello`);
}

export function bye() {
  alert(`Bye`);
}

… ثم يكون الاستيراد الديناميكي هكذا:

let {hi, bye} = await import('./say.js');

hi();
bye();

أو إذا كان say.js يحتوي على التصدير المبدئي:

// ? say.js
export default function() {
  alert("Module loaded (export default)!");
}

… بعد ذلك، من أجل الوصول إليه، يمكننا استخدام الخاصية default لكائن الوِحدة:

let obj = await import('./say.js');
let say = obj.default;
// ‫أو بسطرٍ واحد هكذا:‎
// let {default: say} = await import('./say.js');

say();

إليك المثال الكامل: الملف say.js:

export function hi() {
  alert(`Hello`);
}

export function bye() {
  alert(`Bye`);
}

export default function() {
  alert("Module loaded (export default)!");
}

الملف index.html:

<!doctype html>
<script>
  async function load() {
    let say = await import('./say.js');
    say.hi(); // Hello!
    say.bye(); // Bye!
    say.default(); // Module loaded (export default)!
  }
</script>
<button onclick="load()">Click me</button>

وإليك النتائج في هذا المثال حي.

لاحظ كيف تعمل عمليات الاستيراد الديناميكية في السكربتات العادية، ولا تتطلب script type ="module"‎.

لاحظ أيضًا على الرغم من أن تعليمة import()‎ تشبه طريقة استدعاء دالّة، إلا أنها صياغة خاصة ويحدث هذا التشابه فقط لاستخدام الأقواس (على غرار super ()‎).

لذلك لا يمكننا نسخ تعليمة import إلى متغير، أو استخدام call/apply معها. إذ هي ليست دالّة.

ترجمة -وبتصرف- للفصل Dynamic imports من كتاب The JavaScript language





تفاعل الأعضاء


لا توجد أيّة تعليقات بعد



يجب أن تكون عضوًا لدينا لتتمكّن من التعليق

انشاء حساب جديد

يستغرق التسجيل بضع ثوان فقط


سجّل حسابًا جديدًا

تسجيل الدخول

تملك حسابا مسجّلا بالفعل؟


سجّل دخولك الآن