تقليل حمولات JavaScript من خلال تقسيم الرمز

لا أحد يحب الانتظار. يغادر أكثر من% 50 من المستخدمين الموقع الإلكتروني إذا استغرق تحميله أكثر من 3 ثوانٍ.

يؤثّر إرسال حِزم JavaScript كبيرة في سرعة موقعك الإلكتروني بشكل ملحوظ. بدلاً من إرسال كل رمز JavaScript إلى المستخدم فور تحميل الصفحة الأولى من تطبيقك، يمكنك تقسيم حِزمتك إلى أجزاء متعددة وإرسال ما هو ضروري فقط في البداية.

ما هي فائدة تقسيم الرموز البرمجية؟

إنّ تقسيم الرموز البرمجية هو أسلوب يهدف إلى تقليل وقت بدء التشغيل. عندما نرسل قدرًا أقل من JavaScript عند بدء التشغيل، يمكننا جعل التطبيقات تفاعلية بشكل أسرع من خلال تقليل عمل الخيط الرئيسي خلال هذه الفترة الحرجة.

في ما يتعلّق بمؤشرات أداء الويب الأساسية، سيؤدي تقليل حِزم JavaScript التي يتم تنزيلها عند بدء التشغيل إلى تحسين أوقات مدى استجابة الصفحة لتفاعلات المستخدم (INP). والسبب في ذلك هو أنّه من خلال تحرير سلسلة المحادثات الرئيسية، يتمكّن التطبيق من الاستجابة لبيانات المستخدمين بشكل أسرع من خلال تقليل تكاليف بدء التشغيل المرتبطة بتحليل JavaScript وترجمته وتنفيذه.

استنادًا إلى بنية موقعك الإلكتروني، لا سيما إذا كان موقعك الإلكتروني يعتمد بشكل كبير على العرض من جهة العميل، قد يؤدي تقليل حجم حِزم JavaScript المسؤولة عن عرض العلامات إلى تحسين أوقات سرعة عرض أكبر محتوى مرئي (LCP). يمكن أن يحدث ذلك عندما يتأخّر اكتشاف مَعلمات LCP من قِبل المتصفّح إلى أن يكتمل ترميز جهة العميل، أو عندما يكون الخيط الرئيسي مشغولاً جدًا لعرض عنصر LCP هذا. يمكن أن يؤدّي كلا السيناريوهَين إلى تأخير وقت LCP للصفحة.

القياس

يعرض Lighthouse تقرير تدقيق تعذّر إجراؤه عندما يستغرق تنفيذ كل JavaScript على الصفحة وقتًا طويلاً.

عملية تدقيق في Lighthouse تُظهر أنّ النصوص البرمجية تستغرق وقتًا طويلاً جدًا في التنفيذ

يمكنك تقسيم حِزمة JavaScript لإرسال الرمز المطلوب فقط للمسار الأوّلي عندما يُحمِّل المستخدِم أحد التطبيقات. ويؤدي ذلك إلى تقليل مقدار النص البرمجي الذي يجب تحليله وترجمته، ما يؤدي إلى تقليل أوقات تحميل الصفحة.

تتيح لك حِزم الوحدات الشائعة، مثل webpack و Parcel و Rollup تقسيم الحِزم باستخدام عمليات الاستيراد الديناميكية. على سبيل المثال، راجِع المقتطف التالي من الرمز البرمجي الذي يعرض مثالاً على someFunction يتم تشغيله عند إرسال نموذج.

import moduleA from "library";

form.addEventListener("submit", e => {
  e.preventDefault();
  someFunction();
});

const someFunction = () => {
  // uses moduleA
}

في هذا المثال، يستخدم someFunction وحدة مستورَدة من مكتبة معيّنة. إذا كانت هذه الوحدة غير مستخدَمة في مكان آخر، يمكن تعديل مجموعة الرموز البرمجية لاستخدام عملية إعلام ديناميكية لاستردادها فقط عندما يرسل المستخدم النموذج.

form.addEventListener("submit", e => {
  e.preventDefault();
  import('library.moduleA')
    .then(module => module.default) // using the default export
    .then(() => someFunction())
    .catch(handleError());
});

const someFunction = () => {
    // uses moduleA
}

لا يتم تضمين الرمز الذي يشكّل الوحدة في الحزمة الأولية، ويتم الآن تحميله بشكل بطيء، أو لا يتم تقديمه للمستخدم إلا عند الحاجة إليه بعد إرسال النموذج. لتحسين أداء الصفحة بشكل أكبر، يمكنك تحميل الأجزاء المهمة مسبقًا لتحديد أولوياتها واستردادها في وقت أقرب.

على الرغم من أنّ مقتطف الرمز السابق هو مثال بسيط، إلا أنّ تحميل العناصر الخارجية بشكلٍ بطيء ليس نمطًا شائعًا في التطبيقات الأكبر حجمًا. عادةً ما يتم تقسيم التبعيات التابعة لجهة خارجية إلى حِزمة مورّد منفصلة يمكن تخزينها مؤقتًا لأنّها لا يتم تحديثها بشكل متكرّر. يمكنك الاطّلاع على مزيد من المعلومات حول كيفية استخدام الإضافة SplitChunksPlugin للقيام بذلك.

إنّ التقسيم على مستوى المسار أو المكوّن عند استخدام إطار عمل من جهة العميل هو نهج أبسط لتحميل أجزاء مختلفة من تطبيقك بشكل بطيء. توفّر العديد من الإطارات المرجعية الرائجة التي تستخدِم webpack أدوات تجريدية لتسهيل عملية التحميل المُتأخر بدلاً من الخوض في الإعدادات بنفسك.