تقليل حمولات 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() هي جزء من اقتراح (في المرحلة 3 حاليًا من عملية TC39) لتضمين إمكانية استيراد وحدة نمطية بشكل ديناميكي. وقد أدرجت حزمة الويب دعمًا لهذه الميزة وتتّبع البنية نفسها التي يحدّدها الاقتراح.

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

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

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

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

مراقب

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

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

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

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

لاحظ كيف يتم ترتيب الأرقام أيضًا.

الخاتمة

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

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

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

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

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

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

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

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

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

حيثما أمكن، استفِد من تلميحات المتصفّح، مثل <link rel="preload"> أو <link rel="prefetch">، لمحاولة تحميل الوحدات المهمة في وقت أقرب. يتيح webpack كلا التلميحين من خلال استخدام التعليقات السحرية في عبارات الاستيراد. يتوفّر المزيد من التفاصيل في دليل التحميل المُسبَق للأجزاء المهمة.

التحميل الكسول لأكثر من الرمز

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