إزالة الرموز غير المستخدَمة

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

لقطة شاشة التطبيق

القياس

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

  • لمعاينة الموقع الإلكتروني، اضغط على عرض التطبيق. ثم اضغط على ملء الشاشة ملء الشاشة.

انقر على القطة الصغيرة المفضّلة لديك. يتم استخدام قاعدة بيانات Realtime من Firebase في هذا التطبيق، ولهذا السبب يتم تعديل النتيجة في الوقت الفعلي ويتم مزامنة النتيجة مع كل مستخدم آخر يستخدم التطبيق. 🐈

  1. اضغط على Ctrl ‏+ Shift ‏+ J (أو Command ‏+ Option ‏+ J على نظام التشغيل Mac) لفتح DevTools.
  2. انقر على علامة التبويب الشبكة.
  3. ضَع علامة في مربّع الاختيار إيقاف ذاكرة التخزين المؤقت.
  4. أعِد تحميل التطبيق.

حجم الحزمة الأصلي هو 992 كيلوبايت

يتم تحميل رمز JavaScript بحجم 1 ميغابايت تقريبًا لتحميل هذا التطبيق البسيط.

اطّلِع على تحذيرات المشروع في أدوات المطوّرين.

  • انقر على علامة التبويب وحدة التحكّم.
  • تأكَّد من تفعيل Warnings في القائمة المنسدلة للمستويات بجانب إدخال Filter.

فلتر التحذيرات

  • اطّلِع على التحذير المعروض.

تحذير في وحدة التحكّم

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

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

جارٍ تحليل الحزمة

هناك تبعيتان رئيسيتان في التطبيق:

  • Firebase: منصة تقدّم عددًا من خدمات المفيدة لتطبيقات iOS أو Android أو الويب. في هذه الحالة، يتم استخدام قاعدة بياناته في الوقت الفعلي لتخزين معلومات كل قطة ومزامنتها في الوقت الفعلي.
  • Moment.js: مكتبة أدوات تسهّل التعامل مع التواريخ في JavaScript. يتم تخزين تاريخ ميلاد كل قطة في قاعدة بيانات Firebase، ويتم استخدام moment لاحتساب عمرها بالأسابيع.

كيف يمكن أن تساهم تبعيتان فقط في حجم حِزمة يبلغ 1 ميغابايت تقريبًا؟ حسنًا، أحد الأسباب هو أنّ أيّ تبعية يمكن أن تتضمّن بدورها تبعيات خاصة بها، لذا هناك عدد أكبر بكثير من تبعيتين فقط إذا تمّ أخذ كلّ عمق/فرع من "شجرة" تبعية في الاعتبار. من السهل أن يصبح التطبيق كبيرًا بسرعة نسبية إذا تم تضمين العديد من التبعيات.

قم بتحليل الحزمة للحصول على فكرة أفضل عما يحدث. هناك عدد من الأدوات المختلفة التي أنشأها المنتدى والتي يمكن أن تساعد في إجراء ذلك، مثل webpack-bundle-analyzer.

تم تضمين حزمة هذه الأداة في التطبيق كـ devDependency.

"devDependencies": {
  //...
  "webpack-bundle-analyzer": "^2.13.1"
},

وهذا يعني أنّه يمكن استخدامه مباشرةً في ملف إعدادات webpack. استورِده في بداية webpack.config.js:

const path = require("path");

//...
const BundleAnalyzerPlugin = require("webpack-bundle-analyzer")
  .BundleAnalyzerPlugin;

أضِفه الآن كمكوّن إضافي في نهاية الملف ضمن صفيف plugins:

module.exports = {
  //...
  plugins: [
    //...
    new BundleAnalyzerPlugin()
  ]
};

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

أداة تحليل حِزم Webpack

قد لا يكون هذا الحلّ لطيفًا مثل رؤية بعض القطط 🐱، ولكنه مفيد للغاية. عند تمرير مؤشر الماوس فوق أي من الحِزم، يظهر حجمها بثلاث طُرق مختلفة:

حجم الإحصاءات الحجم قبل أي تصغير أو ضغط
الحجم الذي تم تحليله حجم الحزمة الفعلية داخل الحزمة بعد تجميعها. يعمل الإصدار 4 من webpack (الذي يتم استخدامه في هذا التطبيق) على تصغير الملفات المجمّعة تلقائيًا، ولهذا السبب يكون حجمه أصغر من حجم الإحصاءات.
الحجم بعد ضغطه حجم الحزمة بعد ضغطها باستخدام ترميز gzip يتم تناول هذا الموضوع في دليل منفصل.

باستخدام أداة webpack-bundle-analyzer، من الأسهل تحديد الحِزم غير المستخدَمة أو غير المطلوبة التي تشكّل نسبة كبيرة من الحزمة.

إزالة الحِزم غير المستخدَمة

يُظهر التمثيل البصري أن حزمة firebase تتكوّن من الكثير من مجرّد قاعدة بيانات. تشمل الحزمة حزمًا إضافية مثل:

  • firestore
  • auth
  • storage
  • messaging
  • functions

هذه خدمات رائعة يوفّرها Firebase (يمكنك الرجوع إلى المستندات للحصول على مزيد من المعلومات)، ولكن لا يتم استخدام أي منها في التطبيق، لذلك ليس هناك سبب لاستيرادها كلها.

ألغِ التغييرات في webpack.config.js للاطّلاع على التطبيق مرة أخرى:

  • أزِل BundleAnalyzerPlugin من قائمة الإضافات:
plugins: [
  //...
  new BundleAnalyzerPlugin()
];
  • والآن، أزِل عملية الاستيراد غير المستخدَمة من أعلى الملف:
const path = require("path");

//...
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

من المفترض أن يتم تحميل التطبيق بشكل طبيعي الآن. عدِّل src/index.js لتعديل عمليات استيراد Firebase.

import firebase from 'firebase';
import firebase from 'firebase/app';
import 'firebase/database';

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

تم تقليل حجم الحزمة إلى 480 كيلوبايت

تمت إزالة أكثر من نصف حجم الحزمة. توفّر Firebase العديد من خدمات مختلفة وتمنح المطوّرين خيار تضمين الخدمات التي يحتاجون إليها فقط. في هذا التطبيق، تم استخدام firebase/database فقط لتخزين كل البيانات ومزامنتها. إنّ استيراد firebase/app، الذي يُعدّ واجهة برمجة التطبيقات لكل من الخدمات المختلفة، مطلوب دائمًا.

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

على الرغم من أنّ حجم الحِزمة قد تم خفضه بشكل كبير، لا يزال هناك مزيد من العمل الذي يجب تنفيذه. 😈

إزالة الحِزم غير المطلوبة

على عكس Firebase، لا يمكن استيراد أجزاء من مكتبة moment بسهولة، ولكن هل يمكن إزالتها بالكامل؟

يتم تخزين تاريخ ميلاد كل قطة لطيفة بتنسيق Unix (بالأجزاء من الثانية) في قاعدة بيانات Firebase.

تواريخ الميلاد المخزّنة بتنسيق Unix

هذا هو الطابع الزمني لتاريخ ووقت محدّدَين يتم تمثيلهما بعدد المللي ثانية التي انقضت منذ 1 كانون الثاني (يناير) 1970، 00:00 بالتوقيت العالمي المنسق. إذا كان بالإمكان احتساب التاريخ والوقت الحاليَين بالتنسيق نفسه، يمكن إنشاء وظيفة صغيرة للعثور على عمر كل قطة بالأسابيع.

وكالعادة، ننصحك بعدم النسخ واللصق أثناء اتّباع الخطوات الواردة هنا. ابدأ بمحاولة إزالة moment من عمليات الاستيراد في src/index.js.

import firebase from 'firebase/app';
import 'firebase/database';
import * as moment from 'moment';

هناك مستمع أحداث Firebase يعالج تغييرات القيم في قاعدة بياناتنا:

favoritesRef.on("value", (snapshot) => { ... })

فوق ذلك، أضِف دالة صغيرة لاحتساب عدد الأسابيع من تاريخ معيّن:

const ageInWeeks = birthDate => {
  const WEEK_IN_MILLISECONDS = 1000 * 60 * 60 * 24 * 7;
  const diff = Math.abs((new Date).getTime() - birthDate);
  return Math.floor(diff / WEEK_IN_MILLISECONDS);
}

في هذه الدالة، يتم حساب الفرق بالمللي ثانية بين التاريخ والوقت الحاليين (new Date).getTime() وتاريخ الميلاد (وسيطة birthDate، بالمللي ثانية بالفعل) وقسمته على عدد المللي ثانية في أسبوع واحد.

أخيرًا، يمكن إزالة جميع نُسخ moment في مستمع الأحداث من خلال الاستفادة من هذه الدالة بدلاً من ذلك:

favoritesRef.on("value", (snapshot) => {
  const { kitties, favorites, names, birthDates } = snapshot.val();
  favoritesScores = favorites;

  kittiesList.innerHTML = kitties.map((kittiePic, index) => {
    const birthday = moment(birthDates[index]);

    return `
      <li>
        <img src=${kittiePic} onclick="favKittie(${index})">
        <div class="extra">
          <div class="details">
            <p class="name">${names[index]}</p>
            <p class="age">${moment().diff(birthday, 'weeks')} weeks old</p>
            <p class="age">${ageInWeeks(birthDates[index])} weeks old</p>
          </div>
          <p class="score">${favorites[index]} ❤</p>
        </div>
      </li>
    `})
});

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

تم تقليل حجم الحزمة إلى 225 كيلوبايت

تم تقليل حجم حزمتنا بمقدار أكثر من النصف مرة أخرى.

الخاتمة

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

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

عندما يتعلق الأمر بإزالة المكتبات غير المطلوبة، قد تكون الأمور أكثر صعوبةً. من المهم العمل عن كثب مع فريقك ومعرفة ما إذا كان هناك احتمال لتبسيط أجزاء من قاعدة الرموز. قد يبدو أنّ إزالة moment في هذا التطبيق هو الإجراء المناسب في كل مرة، ولكن ماذا عن المناطق الزمنية واختلافات اللغة التي يجب التعامل معها؟ أو ماذا لو كانت هناك عمليات التلاعب بالتاريخ أكثر تعقيدًا؟ يمكن أن تصبح الأمور معقدة جدًا عند التلاعب بالتواريخ أو الأوقات وتحليلها، وتبسِّط مكتبات مثل moment وdate-fns هذه العملية بشكل كبير.

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