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

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

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

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

القياس

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

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

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

71.2 كيلوبايت من JavaScript لمجرد فرز بعض الأرقام في تطبيق بسيط. فما هو السبب؟

في الرمز المصدر (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) لتضمين إمكانية استيراد وحدة ديناميكيًا. وقد ضمّن 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 كيلوبايت

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

الخاتمة

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

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

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

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

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

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

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

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

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

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

"التحميل الكسول" لأكثر من الرموز البرمجية

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