تحسين تنفيذ JavaScript

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

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

يمكن أن يكون تحليل أداء JavaScript فنًا، لأنّ JavaScript الذي تكتبه ليس مثل الرمز البرمجي الذي يتم تنفيذه فعليًا. تستخدِم المتصفّحات الحديثة أدوات تجميع أثناء التنفيذ (JIT) وجميع أنواع التحسينات والخدع لمحاولة تقديم أسرع تنفيذ ممكن، ويؤدي ذلك إلى تغيير ديناميكية الرمز بشكلٍ كبير.

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

ملخّص

  • تجنَّب استخدام setTimeout أو setInterval لإجراء تعديلات مرئية، واستخدِم دائمًا requestAnimationFrame بدلاً من ذلك.
  • نقل JavaScript الذي يستغرق وقتًا طويلاً من سلسلة التعليمات الرئيسية إلى Web Workers
  • استخدِم المهام الصغيرة لإجراء تغييرات على DOM على مدار عدة إطارات.
  • استخدِم "المخطط الزمني" و"أداة تحليل JavaScript" في أدوات مطوّري البرامج في Chrome لتقييم تأثير JavaScript.

استخدِم requestAnimationFrame للتغييرات المرئية.

عندما تحدث تغييرات مرئية على الشاشة، عليك تنفيذ عملك في الوقت المناسب للسماح للمتصفّح بمعالجة التغييرات، وهو في بداية اللقطة مباشرةً. إنّ الطريقة الوحيدة لضمان تنفيذ JavaScript في بداية اللقطة هي استخدام requestAnimationFrame.

/**
    * If run as a requestAnimationFrame callback, this
    * will be run at the start of the frame.
    */
function updateScreen(time) {
    // Make visual updates here.
}

requestAnimationFrame(updateScreen);

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

يؤدي setTimeout إلى عدم عرض إطار في المتصفّح.

في الواقع، كان jQuery يستخدم setTimeout لسلوك animate. تم تغييره لاستخدام requestAnimationFrame في الإصدار 3. إذا كنت تستخدم إصدارًا قديمًا من jQuery، يمكنك تصحيحه لاستخدام requestAnimationFrame، وهو ما ننصح به بشدة.

تقليل التعقيد أو استخدام Web Workers

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

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

في كثير من الحالات، يمكنك نقل العمل الحسابي البحت إلى Web Workers، إذا لم يكن يتطلّب الوصول إلى DOM، على سبيل المثال. غالبًا ما يكون التلاعب بالبيانات أو التنقّل فيها، مثل الترتيب أو البحث، مناسبًا لهذا النموذج، كما هو الحال مع التحميل وإنشاء النماذج.

var dataSortWorker = new Worker("sort-worker.js");
dataSortWorker.postMesssage(dataToSort);

// The main thread is now free to continue working on other things...

dataSortWorker.addEventListener('message', function(evt) {
    var sortedData = evt.data;
    // Update data on screen...
});

لا يمكن أن يناسب هذا النموذج كل الأعمال: لا يمكن لـ Web Workers الوصول إلى DOM. إذا كان يجب أن يكون عملك على سلسلة المهام الرئيسية، ننصحك باستخدام أسلوب تجميع، حيث تقسم المهمة الأكبر إلى مهام صغيرة لا تستغرق كلّ منها أكثر من بضع مللي ثوانٍ، ويتم تنفيذها داخل معالجات requestAnimationFrame في كل إطار.

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

التعرّف على "رسوم عرض اللقطات" في JavaScript

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

لوحة "الأداء" في "أدوات مطوّري البرامج في Chrome" هي أفضل طريقة لقياس تكلفة JavaScript. عادةً ما تحصل على سجلّات من المستوى الأدنى على النحو التالي:

تسجيل أداء في "أدوات مطوّري البرامج في Chrome"

يقدّم القسم الرئيسي رسمًا بيانيًا للمسارات لطلبات JavaScript حتى تتمكّن من تحليل الدوالّ التي تمّ استدعاؤها بالضبط والمدة التي استغرقتها كلّ وظيفة.

باستخدام هذه المعلومات، يمكنك تقييم تأثير برمجة ‎JavaScript في أداء تطبيقك، والبدء في العثور على أي نقاط ساخنة تتطلّب تنفيذ الدوالّ وقتًا طويلاً وإصلاحها. كما ذكرنا سابقًا، عليك محاولة إزالة JavaScript الذي يستغرق وقتًا طويلاً أو نقله إلى Web Worker إذا لم يكن ذلك ممكنًا، ما يُتيح للسلسلة الرئيسية مواصلة تنفيذ المهام الأخرى.

اطّلِع على مقالة البدء بتحليل أداء وقت التشغيل للتعرّف على كيفية استخدام لوحة "الأداء".

تجنُّب إجراء تحسينات دقيقة على JavaScript

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

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

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