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

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

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

القياس

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

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

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

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

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

يتم إرسال ما يقرب من 1 ميغابايت من JavaScript لتحميل هذا التطبيق البسيط.

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

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

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

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

تحذير في Play Console

‫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 Bundle Analyzer

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

حجم الإحصاءات الحجم قبل أي عملية تصغير أو ضغط
Parsed size حجم الحزمة الفعلية داخل حزمة التطبيق بعد تجميعها يصغّر الإصدار 4 من webpack (المستخدَم في هذا التطبيق) حجم الملفات المجمّعة تلقائيًا، ولهذا السبب يكون هذا الحجم أصغر من حجم الإحصاءات.
الحجم المضغوط بتنسيق Gzip حجم الحزمة بعد ضغطها باستخدام ترميز 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';

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

تم تقليل حجم الحزمة إلى 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 على تبسيط ذلك بشكل كبير.

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