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

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

يوضّح هذا الدرس التطبيقي حول الترميز كيفية استخدام تقسيم الرمز لتحسين أداء تطبيق بسيط يرتب ثلاثة أرقام.

نافذة متصفح تعرض تطبيقًا بعنوان Magic Sorter (أداة الترتيب السحرية) مع ثلاثة حقول لإدخال الأرقام وزر فرز.

قياس

كما هو الحال دائمًا، من المهم أولاً قياس مستوى أداء موقع الويب قبل محاولة إضافة أي تحسينات.

  1. لمعاينة الموقع الإلكتروني، اضغط على عرض التطبيق، ثم اضغط على ملء الشاشة ملء الشاشة.
  2. اضغط على "Control+Shift+J" (أو "Command+Option+J" على نظام التشغيل Mac) لفتح "أدوات مطوّري البرامج".
  3. انقر على علامة التبويب الشبكة.
  4. ضع علامة في مربّع الاختيار إيقاف ذاكرة التخزين المؤقت.
  5. أعِد تحميل التطبيق.

لوحة شبكة تعرض حزمة JavaScript بحجم 71.2 كيلوبايت

71.2 كيلوبايت من JavaScript فقط لترتيب بضعة أرقام في تطبيق بسيط. What gives?

في رمز المصدر (src/index.js)، يتم استيراد مكتبة lodash واستخدامها في هذا التطبيق. توفر Lodash العديد من دوال الفائدة المفيدة، ولكن يتم استخدام طريقة واحدة فقط من الحزمة هنا. يُعد تثبيت واستيراد تبعيات كاملة تابعة لجهات خارجية عند استخدام جزء صغير منها خطأ شائعًا.

تحسين

هناك عدة طرق يمكن من خلالها اقتطاع حجم الحزمة:

  1. كتابة طريقة فرز مخصصة بدلاً من استيراد مكتبة تابعة لجهة خارجية
  2. استخدام طريقة Array.prototype.sort() المضمَّنة للترتيب رقميًا
  3. استيراد الطريقة sortBy فقط من lodash وليس من المكتبة بأكملها
  4. قم بتنزيل رمز الفرز فقط عندما ينقر المستخدم على الزر

يُعد الخياران 1 و2 طريقتين مناسبتين تمامًا لتقليل حجم الحزمة (ومن المحتمل أن يكونا الأنسب لتطبيق حقيقي). ومع ذلك، لا تُستخدم هذه المعلومات في هذا البرنامج التعليمي لأغراض التدريس 😈.

يساعد كلا الخيارين 3 و4 في تحسين أداء هذا التطبيق. تتناول الأقسام القليلة التالية من هذا الدرس التطبيقي حول الترميز هذه الخطوات. مثل أي برنامج تعليمي للترميز، حاول دائمًا كتابة التعليمات البرمجية بنفسك بدلاً من نسخها ولصقها.

استيراد ما تحتاج إليه فقط

يجب تعديل بعض الملفات لاستيراد الطريقة الفردية فقط من lodash. للبدء، استبدِل هذه التبعية في package.json:

"lodash": "^4.7.0",

بالإضافة إلى ما يلي:

"lodash.sortby": "^4.7.0",

الآن في src/index.js، استيراد هذه الوحدة المحددة:

import "./style.css";
import _ from "lodash";
import sortBy from "lodash.sortby";

وعدِّل كيفية ترتيب القيم:

form.addEventListener("submit", e => {
  e.preventDefault();
  const values = [input1.valueAsNumber, input2.valueAsNumber, input3.valueAsNumber];
  const sortedValues = _.sortBy(values);
  const sortedValues = sortBy(values);

  results.innerHTML = `
    <h2>
      ${sortedValues}
    </h2>
  `
});

أعِد تحميل التطبيق وافتَح "أدوات مطوري البرامج" وألقِ نظرة على لوحة الشبكة مرة أخرى.

لوحة شبكة تعرض حزمة JavaScript بحجم 15.2 كيلوبايت

بالنسبة إلى هذا التطبيق، تم تقليل حجم الحزمة بمقدار أكثر من 4 أضعاف مع العمل قليل جدًا، ولكن لا يزال هناك مجال أكبر للتحسين.

تقسيم الرمز

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

يمكن تقسيم الحزمة الواحدة المستخدمة في هذا التطبيق إلى قسمين منفصلين:

  • واحد مسئول عن التعليمات البرمجية التي تشكل مسارنا الأولي
  • مقطع ثانوي يحتوي على تعليمة الفرز الخاصة بنا

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

ابدأ بإزالة استيراد المستوى الأعلى لطريقة الترتيب في src/index.js:

import sortBy from "lodash.sortby";

وعليك استيراده ضمن وحدة معالجة الحدث التي يتم تنشيطها عند الضغط على الزرّ:

form.addEventListener("submit", e => {
  e.preventDefault();
  import('lodash.sortby')
    .then(module => module.default)
    .then(sortInput())
    .catch(err => { alert(err) });
});

والميزة import() هي جزء من اقتراح (حاليًا في المرحلة الثالثة من عملية TC39) لتضمين إمكانية استيراد وحدة بشكل ديناميكي. لقد سبق وقدّمت حزمة Webpack دعمًا لهذا الاقتراح، وتتّبع البنية نفسها التي وضّحها الاقتراح.

تعرض import() وعدًا، وعندما يتم حلها، يتم توفير الوحدة المحددة التي يتم تقسيمها إلى مقطع منفصل. بعد عرض الوحدة، يُستخدَم module.default للإشارة إلى عملية التصدير التلقائية التي يوفّرها lodash. يتم سلسلة الوعد بسلسلة .then أخرى تستدعي الطريقة sortInput لفرز قيم الإدخال الثلاث. في نهاية سلسلة الوعد،يتم استخدام السمة catch() لمعالجة الحالات التي يتم فيها رفض الوعد بسبب خطأ.

آخر ما يجب فعله هو كتابة الطريقة sortInput في نهاية الملف. يجب أن تكون هذه دالة تعرض دالة تأخذ الطريقة التي تم استيرادها من lodash.sortBy. يمكن للدالة المتداخلة بعد ذلك فرز قيم الإدخال الثلاث وتحديث DOM.

const sortInput = () => {
  return (sortBy) => {
    const values = [
      input1.valueAsNumber,
      input2.valueAsNumber,
      input3.valueAsNumber
    ];
    const sortedValues = sortBy(values);

    results.innerHTML = `
      <h2>
        ${sortedValues}
      </h2>
    `
  };
}

المراقبة

أعِد تحميل التطبيق للمرة الأخيرة وتابِع لوحة الشبكة عن كثب مرة أخرى. لا يتم تنزيل سوى حزمة أوّلية صغيرة بمجرد تحميل التطبيق.

لوحة شبكة تعرض حزمة JavaScript بحجم 2.7 كيلوبايت

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

لوحة شبكة تعرض حزمة JavaScript بحجم 2.7 كيلوبايت تليها حزمة JavaScript بحجم 13.9 كيلوبايت

لاحظ كيف لا يزال يتم فرز الأرقام!

الخلاصة

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

واجهة مستخدم تتيح التحميل الكسول

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

يتم التحميل الكسول لوحدات العُقد التابعة لجهات خارجية.

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

طريقة "التحميل الكسول" باستخدام إطار عمل JavaScript

إنّ العديد من إطارات العمل والمكتبات الشائعة التي تستخدم حزمة الويب توفّر رسومات تجريدية لتسهيل عملية "التحميل الكسول" مقارنةً باستخدام عمليات الاستيراد الديناميكية في منتصف التطبيق.

على الرغم من أهمية فهم آلية عمل عمليات الاستيراد الديناميكية، عليك دائمًا استخدام الطريقة التي يقترحها إطار العمل/المكتبة لتحميل وحدات معيّنة باستخدام طريقة \"التحميل الكسول\".

التحميل المُسبق والجلب المسبق

استفد متى أمكن من تلميحات المتصفح مثل <link rel="preload"> أو <link rel="prefetch"> لمحاولة تحميل الوحدات المهمة في وقت أسرع. وتتيح حزمة الويب استخدام التلميحات عبر استخدام التعليقات السحرية في عبارات الاستيراد. يمكنك الاطّلاع على مزيد من التفاصيل في دليل تحميل المقاطع المهمة مسبقًا.

طريقة "التحميل الكسول" تتجاوز قيمة الرمز البرمجي

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