إن طريقة الاستيراد والتصدير التي تحدثنا عنها في الفصل السابق، تصدير الوحدات واستيرادها تدعى بالطريقة "الثابتة". إذ أنّ صياغتها بسيطة وصارمة للغاية.
دعنا في البداية نوضح بعض الأمور، أولًا، لا يمكننا إنشاء أي وسطاء للتعليمة 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
أفضل التعليقات
لا توجد أية تعليقات بعد
انضم إلى النقاش
يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.