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

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

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

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

القياس

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

  1. لمعاينة الموقع الإلكتروني، اضغط على عرض التطبيق. ثم اضغط على ملء الشاشة ملء الشاشة.
  2. اضغط على Ctrl ‏+ Shift ‏+ J (أو Command ‏+ Option ‏+ J على نظام التشغيل Mac) لفتح DevTools.
  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) لتضمين إمكانية استيراد وحدة ديناميكيًا. سبق أن ضمّن 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

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

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

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

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

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

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