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

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

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

القياس

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

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

انقر على القطة الصغيرة المفضّلة لديك. يتم استخدام قاعدة بيانات 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 Bundle Analyzer

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

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

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