تا اینجا یاد گرفتیم چطور ماژولها را در بالای فایل خود به صورت ثابت (Static) وارد کنیم. اما در پروژههای بزرگ، لود کردن همه کدها در همان ابتدای کار عاقلانه نیست و سرعت سایت را پایین میآورد. در این بخش یاد میگیریم چطور کدها را فقط «در زمان نیاز» بارگذاری کنیم و چطور عملیاتهای ناهمگام (Asynchronous) را در سطح اصلی ماژول مدیریت کنیم.
یکی از کاربردیترین قابلیتهای جاوااسکریپت مدرن، امکان بارگذاری دینامیک ماژولهاست. این ویژگی به شما اجازه میدهد ماژولها را دقیقاً زمانی که به آنها نیاز دارید لود کنید، نه اینکه همه چیز را همان اول بارگذاری و حافظه را اشغال کنید. این کار برگ برندهای برای افزایش سرعت و بهینهسازی (Performance) سایت شماست.
import()در این روش، شما از import() به عنوان یک تابع استفاده میکنید و آدرس ماژول را به عنوان پارامتر به آن میدهید. از آنجا که لود شدن فایل زمانبر است، این تابع یک پرامیس (Promise) برمیگرداند. این پرامیس در صورت موفقیت، یک «شیء ماژول» (Module Object) شامل تمام خروجیهای آن فایل را در اختیارتان میگذارد:
import("./modules/myModule.js").then((module) => {
// اینجا کارهایی که میخواهی با ماژول انجام بدهی را مینویسی
});
یک نکته امنیتی و فنی: استفاده از
import()دینامیک در ترد اصلی مرورگر (Main Thread) و همچنین در وبورکرها (Shared & Dedicated Workers) کاملاً مجاز است؛ اما اگر آن را درون یک Service Worker یا Worklet صدا بزنید، خطا میدهد.
تصور کنید یک صفحه وب داریم که قرار نیست در ابتدای لود شدن هیچ شکلی روی بوم بکشد. در عوض، ۳ دکمه به نامهای "Circle" ،"Square" و "Triangle" داریم. میخواهیم کاری کنیم که کاربر روی هر دکمهای کلیک کرد، ماژول مربوط به آن شکل تازه همانجا لود شود و شکل را رسم کند!
برای این کار در فایل main.js ابتدا دکمهها را میگیریم:
const squareBtn = document.querySelector(".square");
سپس یک گوشبهزنگ (Event Listener) روی دکمه میگذاریم تا با کلیک کاربر، فایل مربوطه لود شود:
squareBtn.addEventListener("click", () => {
// بارگذاری دینامیک فایل square.js با کلیک کاربر
import("./modules/square.js").then((Module) => {
const square = new Module.Square(
myCanvas.ctx,
myCanvas.listId,
50,
50,
100,
"blue",
);
square.draw();
square.reportArea();
square.reportPerimeter();
});
});
نکته: از آنجا که خروجی پرامیس یک «شیء ماژول» است، کلاسی که صادر کرده بودیم حالا زیرمجموعه این شیء قرار میگیرد. به همین دلیل برای ساخت نمونه، کلمه
Module.را قبل از نام کلاس میآوریم؛ یعنی:Module.Square.
یکی از بزرگترین فواید import() دینامیک این است که در همهجا (حتی اسکریپتهای معمولی) کار میکند! یعنی اگر یک تگ <script> قدیمی در HTML دارید که ویژگی type="module" ندارد، کماکان میتوانید کدهای ماژولار خود را به صورت دینامیک درون آن لود و استفاده کنید:
<script>
// این یک اسکریپت معمولی است، اما به راحتی ماژول را لود میکند!
import("./modules/square.js").then((module) => {
// کار با ماژول
});
// بقیه کدهای قدیمی پروژه که هنوز به ماژول تبدیل نشدهاند
var btn = document.querySelector(".square");
</script>
به طور سنتی در جاوااسکریپت، شما فقط زمانی میتوانستید از کلمه کلیدی await استفاده کنید که داخل یک تابع ناهمگام (async function) بودید. اما در دنیای ماژولها، قابلیتی به نام Top-level await وجود دارد؛ این یعنی شما میتوانید از await مستقیماً در سطح اصلی کد (خارج از هرگونه تابعی) استفاده کنید!
با این ویژگی، ماژول شما شبیه به یک تابع ناهمگامِ بزرگ عمل میکند؛ یعنی کدهای آن قبل از اینکه در فایلهای دیگر استفاده شوند، پردازش و نهایی میشوند، بدون اینکه لود شدن بقیه فایلهای همردیف (Sibling Modules) را قفل کنند.
فرض کنید پالت رنگی خود را در یک فایل جداگانه به نام colors.json ذخیره کردهایم:
{
"yellow": "#F4D03F",
"green": "#52BE80",
"blue": "#5499C7",
"red": "#CD6155"
}
حالا یک ماژول به نام getColors.js میسازیم تا با یک درخواست fetch این فایل را لود کند و دادهها را برگرداند:
/* modules/getColors.js */
// ارسال درخواست برای گرفتن فایل json
const colors = fetch("../data/colors.json").then((response) => response.json());
// جادوی تراز اول: صادر کردن نتیجه بعد از حل شدن پرامیس
export default await colors;
به خط آخر دقت کنید! ما از کلمه کلیدی await قبل از ثابت colors استفاده کردیم. این یعنی هر ماژول دیگری که این فایل را import کند، صبر میکند تا دانلود و پردازش فایل JSON به طور کامل تمام شود و سپس کارش را شروع میکند.
حالا در فایل main.js خیلی راحت و بدون درگیر شدن با کدهای ناهمگام، پالت رنگی را وارد و استفاده میکنیم:
import colors from "./modules/getColors.js";
import { Canvas } from "./modules/canvas.js";
// استفاده از رنگهای لود شده به صورت مستقیم
const square = new Module.Square(myCanvas.ctx, myCanvas.listId, 50, 50, 100, colors.blue);
const circle = new Module.Circle(myCanvas.ctx, myCanvas.listId, 75, 200, 100, colors.green);
این معماری دو مزیت بزرگ دارد:
کدهای فایل main.js تا زمانی که رنگها کاملاً لود نشوند اجرا نخواهند شد، پس خیالتان راحت است که هیچوقت با متغیرهای خالی یا undefined روبهرو نمیشوید.
این صبر کردن، مسدودکننده (Blocking) نیست! یعنی در همان لحظهای که مرورگر منتظر دریافت فایل رنگهاست، فایلهای دیگر پروژه (مثل canvas.js) به بارگذاری خود در پسزمینه ادامه میدهند و وقت کاربر تلف نمیشود.
این محتوا کاملا رایگان توسط تیم کدلپر ترجمه شده و در اختیار شما کاربران عزیز قرار گرفته است، هر گونه کپی برداری برای مقاصد غیر رایگان و بدون ذکر منبع، مورد پیگیری قانونی قرار میگیرد.
ترجمه شده از منبع: https://developer.mozilla.org/en-US/docs/Web/JavaScript