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

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

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

قياس

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

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

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

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

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

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

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

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

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

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

تحذير بشأن وحدة التحكّم

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

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

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

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

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

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

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

سبق أن تم تضمين حزمة هذه الأداة في التطبيق على أنّها devDependency.

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

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

const path = require("path");

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

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

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

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

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

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

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

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