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

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

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

ما فائدة تقسيم الرمز؟

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

في ما يتعلّق بمؤشرات أداء الويب الأساسية، يساهم تقليل حمولات JavaScript التي يتم تنزيلها عند بدء التشغيل في تحسين أوقات مهلة الاستجابة لأوّل إدخال (FID) ومدى استجابة الصفحة لتفاعلات المستخدم (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 للقيام بذلك.

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