Dynamic Import: بارگذاری ماژولها فقط زمانی که واقعاً نیاز است
در درسهای قبل، تمام import ها بهصورت static (ایستا) بودند: در بالای فایل نوشته میشدند و صرفنظر از اینکه آن ماژول واقعاً استفاده شود یا نه، همراه با بارگذاری صفحه دانلود میشدند. Dynamic Import روشی جایگزین ارائه میدهد که در آن، یک ماژول فقط در لحظهای که کد به آن نیاز دارد، بارگذاری میشود؛ تکنیکی که تأثیر مستقیمی بر سرعت اولیهی بارگذاری صفحات وب دارد.
محدودیت import ایستا
دستور import که تا اینجا دیدهاید، فقط در بالای فایل و بهصورت ایستا قابل نوشتن است؛ نمیتوان آن را داخل یک شرط، حلقه یا تابع قرار داد:
if (userWantsChart) {
import { renderChart } from "./chart.js"; // خطای نحوی؛ مجاز نیست
}
این محدودیت به این دلیل وجود دارد که موتور جاوااسکریپت، تمام import های ایستا را پیش از اجرای هر کد دیگری در فایل، تحلیل و بارگذاری میکند؛ به همین دلیل قرار دادن آن داخل یک شرط، از نظر منطقی معنا ندارد.
تابع import() برای بارگذاری پویا
برای بارگذاری یک ماژول در زمان اجرا و بر اساس شرایط خاص، از تابع import() استفاده میشود که برخلاف دستور import، مثل یک تابع معمولی فراخوانی میشود و یک Promise برمیگرداند:
از آنجا که import() یک Promise برمیگرداند، ترکیب آن با await یا then کاملاً طبیعی است و از همان مفاهیمی استفاده میکند که در فصلهای Promise و async/await بهطور کامل بررسی شد.
ساختار مقدار بازگشتی
مقداری که import() پس از resolve شدن برمیگرداند، شیئی است که شامل تمام named exportهای آن ماژول است، و اگر ماژول default export نیز داشته باشد، از طریق ویژگی default در دسترس است:
// فایل chart.js
export default function renderChart() {
console.log("نمودار رسم شد");
}
export const CHART_VERSION = "2.0";
import("./chart.js").then((module) => {
module.default(); // فراخوانی default export
console.log(module.CHART_VERSION); // "2.0"
});
کاربرد اصلی: Code Splitting برای بهبود سرعت بارگذاری
یکی از مهمترین کاربردهای عملی Dynamic Import، تقسیم کد (code splitting) است: بهجای بارگذاری تمام کد برنامه در همان ابتدا، بخشهایی که فقط در شرایط خاصی مورد نیازند—مثل یک ماژول ویرایشگر متن پیشرفته که فقط با کلیک روی یک دکمهی خاص لازم میشود—تا لحظهی واقعی نیاز، بارگذاری نمیشوند:
const editorButton = document.querySelector("#openEditor");
editorButton.addEventListener("click", async () => {
const { RichTextEditor } = await import("./rich-text-editor.js");
const editor = new RichTextEditor();
editor.mount();
});
در این الگو، اگر کاربر هرگز روی دکمهی ویرایشگر کلیک نکند، حجم نسبتاً سنگین کد آن ماژول اصلاً دانلود نمیشود؛ این موضوع میتواند زمان بارگذاری اولیهی صفحه را بهطور محسوسی کاهش دهد.
مدیریت خطا در Dynamic Import
از آنجا که import() یک Promise معمولی برمیگرداند، در صورت وجود مشکلاتی مثل آدرس نادرست فایل یا خطای شبکه، مدیریت خطا دقیقاً مانند هر Promise دیگری با try/catch یا catch انجام میشود:
try {
const module = await import("./nonexistent-module.js");
} catch (error) {
console.log("بارگذاری ماژول با شکست مواجه شد:", error.message);
}
جدول مقایسهی Import ایستا و Dynamic Import
| ویژگی | import ایستا | import() پویا |
|---|---|---|
| زمان بارگذاری | در همان ابتدا، همراه با بارگذاری فایل | فقط در لحظهی فراخوانی |
| محل استفاده | فقط در بالای فایل، خارج از هر شرط یا تابع | در هر نقطهای از کد، از جمله داخل شرط یا رویداد |
| مقدار بازگشتی | مستقیماً نامهای importشده | یک Promise که به شیء ماژول resolve میشود |
| کاربرد اصلی | وابستگیهای همیشگی و ضروری فایل | Code Splitting و بارگذاری بر اساس نیاز واقعی |
نکات کلیدی
- import ایستا فقط در بالای فایل قابل نوشتن است و نمیتواند داخل شرط یا تابع قرار گیرد.
- تابع import() یک ماژول را در زمان اجرا بارگذاری میکند و یک Promise برمیگرداند.
- مقدار resolveشده از import() شامل تمام named export ها و default export (از طریق ویژگی default) است.
- کاربرد اصلی Dynamic Import، تقسیم کد برای کاهش زمان بارگذاری اولیهی صفحه است.
جمعبندی
Dynamic Import با تابع import()، امکان بارگذاری ماژولها را فقط در لحظهی نیاز واقعی فراهم میکند، برخلاف import ایستا که همیشه در همان ابتدای اجرای برنامه بارگذاری میشود. این تکنیک، ابزار اصلی پیادهسازی code splitting در برنامههای بزرگ است و مستقیماً به بهبود سرعت بارگذاری اولیهی صفحات وب کمک میکند. با تکمیل این درس، مفاهیم اصلی ماژولهای ES در این دوره پوشش داده شده است.
