واجهة برمجة التطبيقات لتوقيت المستخدم

فهم تطبيق الويب

Alex Danilo

إنّ تطبيقات الويب ذات الأداء العالي ضرورية لتوفير تجربة رائعة للمستخدم. مع تزايد تعقيد تطبيقات الويب، من المهم فهم تأثير الأداء في توفير تجربة رائعة. على مدار السنوات القليلة الماضية، ظهر عدد من واجهات برمجة التطبيقات المختلفة في المتصفّح للمساعدة في تحليل أداء الشبكة وأوقات التحميل وما إلى ذلك، ولكنّ هذه الواجهات لا تقدّم بالضرورة تفاصيل دقيقة مع مرونة كافية للعثور على ما يبطئ أداء تطبيقك. أدخِل User Timing API التي توفّر آلية يمكنك استخدامها لقياس حالة تطبيق الويب من أجل تحديد الوقت الذي يقضيه تطبيقك. سنتناول في هذه المقالة واجهة برمجة التطبيقات بالإضافة إلى أمثلة على كيفية استخدامها.

لا يمكنك تحسين ما لا يمكنك قياسه

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

وقت دقة عالية وnow()

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

للحصول على الوقت الحالي في تطبيق الويب، استخدِم الطريقة now() التي تشكّل إضافة لواجهة الأداء. توضّح التعليمة البرمجية التالية كيفية إجراء ذلك:

var myTime = window.performance.now();

هناك واجهة أخرى تُسمى PerformanceTiming تقدّم عددًا من الأوقات المختلفة ذات الصلة بطريقة تحميل تطبيق الويب. تُعرِض طريقة now() الوقت المنقضي منذ حدوث وقت navigationStart في PerformanceTiming.

النوع DOMHighResTimeStamp

عند محاولة تحديد وقت لتطبيقات الويب في الماضي، كنت تستخدم شيئًا مثل Date.now() الذي يعرض DOMTimeStamp. تعرض الدالة DOMTimeStamp عددًا صحيحًا بالمللي ثانية كقيمة لها. لتوفير الدقة العالية المطلوبة لوقت "دقة عالية"، تمّ تقديم نوع جديد يُسمّى DOMHighResTimeStamp. هذا النوع هو قيمة نقطة عائمة تُرجع أيضًا الوقت بالملي ثانية. ولكن نظرًا لأنها النقطة العائمة، يمكن أن تمثل القيمة أجزاء من المللي ثانية، وبالتالي يمكن أن ينتج عنها دقة تبلغ ألف من المللي ثانية.

واجهة "وقت المستخدِم"

والآن بعد أن حصلنا على الطوابع الزمنية العالية الدقة، لنستخدم واجهة توقيت المستخدِم لاستخراج معلومات التوقيت.

توفّر واجهة "توقيت المستخدِم" وظائف تتيح لنا استدعاء طُرق في مواضع مختلفة من تطبيقنا يمكن أن تقدّم مسارًا لمسار التنقّل على غرار مسار Hansel and Gretel للسماح لنا بتتبُّع الوقت الذي يتمّ قضاؤه.

جارٍ استخدام mark()

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

من خلال استدعاء mark() في مواضع مختلفة من تطبيقك، يمكنك معرفة الوقت الذي استغرقته للوصول إلى هذه "العلامة" في تطبيق الويب.

تشير المواصفة إلى عدد من الأسماء المقترَحة للعلامات التي قد تكون مثيرة للاهتمام وتوضّح نفسها بنفسها إلى حدٍ ما، مثل mark_fully_loaded وmark_fully_visible وmark_above_the_fold وما إلى ذلك.

على سبيل المثال، يمكننا وضع علامة تشير إلى وقت تحميل التطبيق بالكامل باستخدام الرمز التالي:

window.performance.mark('mark_fully_loaded');

من خلال ضبط علامات مُسمّاة في تطبيق الويب، يمكننا جمع مجموعة كاملة من بيانات التوقيت وتحليلها في أي وقت لنعرف ما يفعله التطبيق ومتى.

احتساب القياسات باستخدام measure()

بعد ضبط مجموعة من علامات التوقيت، ستحتاج إلى معرفة الوقت المنقضي بينها. ويمكنك استخدام طريقة measure() لإجراء ذلك.

تحتسب طريقة measure() الوقت المنقضي بين العلامات، ويمكنها أيضًا قياس الوقت بين علامتك وأيّ من أسماء الأحداث المعروفة في واجهة PerformanceTiming.

على سبيل المثال، يمكنك احتساب الوقت المستغرَق من اكتمال نموذج DOM إلى تحميل حالة تطبيقك بالكامل باستخدام رمز مثل:

window.performance.measure('measure_load_from_dom', 'domComplete', 'mark_fully_loaded');

عند استدعاء الدالة measure()، يتم تخزين النتيجة بشكل مستقل عن العلامات التي حدّدتها، لكي تتمكّن من استردادها لاحقًا. من خلال تخزين أوقات الاستراحة أثناء تشغيل تطبيقك، يظل التطبيق سريع الاستجابة، ويمكنك تفريغ جميع البيانات بعد انتهاء تطبيقك من بعض الأعمال لكي يتم تحليلها لاحقًا.

تجاهل العلامات باستخدام clearMarks()

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

يمكنك بسهولة التخلص من أي علامات أعددتها من خلال الاتصال بالرقم clearMarks().

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

window.performance.clearMarks();

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

window.performance.clearMarks('mark_fully_loaded');

تؤدي هذه العملية إلى إزالة العلامة التي ضبطناها في المثال الأول مع ترك أي علامات أخرى ضبطناها بدون تغيير.

وقد تحتاج إلى التخلص من أي إجراءات اتّخذتها أيضًا، وهناك طريقة مناسبة لذلك تُسمى clearMeasures(). تعمل هذه الميزة بالطريقة نفسها التي تعمل بها clearMarks()، ولكن بدلاً من ذلك، تعمل على أي قياسات أجريتها. على سبيل المثال، الرمز:

window.performance.clearMeasures('measure_load_from_dom');

سيؤدي ذلك إلى إزالة الإجراء الذي اتخذناه في المثال measure() أعلاه. إذا أردت إزالة جميع المقاييس، تعمل الدالة بالطريقة نفسها التي تعمل بها الدالة clearMarks()، أي أنّك تستدعي الدالة clearMeasures() بدون وسيطات.

الحصول على بيانات التوقيت

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

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

الرمز البرمجي أدناه:

var items = window.performance.getEntriesByType('mark');

يعرض لنا قائمة بجميع العلامات التي تمّ تسجيلها في تطبيق الويب، في حين أنّ الرمز:

var items = window.performance.getEntriesByType('measure');

تُعيد إلينا قائمة بجميع الإجراءات التي اتّخذناها.

يمكنك أيضًا استرداد قائمة بالإدخالات باستخدام الاسم المحدّد الذي قدّمته لها. على سبيل المثال، الرمز:

var items = window.performance.getEntriesByName('mark_fully_loaded');

سيعرض لنا هذا الطلب قائمة تحتوي على عنصر واحد يتضمّن الطابع الزمني mark_fully_loaded في السمة startTime.

تحديد توقيت طلب XHR (مثال)

الآن بعد أن حصلنا على صورة جيدة لواجهة برمجة التطبيقات User Timing API، يمكننا استخدامها لتحليل الوقت الذي تستغرقه جميع طلبات XMLHttpRequest في تطبيق الويب.

سنعدّل أولاً جميع طلبات send() لإصدار طلب استدعاء دالة يُعدّ العلامات، وفي الوقت نفسه سنغيّر طلبات الاستدعاء الناجحة من خلال طلب استدعاء دالة يُعدّ علامة أخرى ثمّ يُنشئ مقياسًا للمدة التي استغرقها الطلب.

لذلك، سيبدو XMLHttpRequest عادةً على النحو التالي:

var myReq = new XMLHttpRequest();
myReq.open('GET', url, true);
myReq.onload = function(e) {
  do_something(e.responseText);
}
myReq.send();

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

var reqCnt = 0;

var myReq = new XMLHttpRequest();
myReq.open('GET', url, true);
myReq.onload = function(e) {
  window.performance.mark('mark_end_xhr');
  reqCnt++;
  window.performance.measure('measure_xhr_' + reqCnt, 'mark_start_xhr', 'mark_end_xhr');
  do_something(e.responseText);
}
window.performance.mark('mark_start_xhr');
myReq.send();

ينشئ الرمز البرمجي أعلاه مقياسًا بقيمة اسم فريدة لكل طلب XMLHttpRequest نرسله. نفترض أنّه يتم تنفيذ الطلبات بالتسلسل، ويجب أن يكون رمز الطلبات الموازية أكثر تعقيدًا قليلاً لمعالجة الطلبات التي يتم عرضها بترتيب غير منتظم، وسنترك ذلك كتدريب للقارئ.

بعد أن يُجري تطبيق الويب مجموعة من الطلبات، يمكننا تفريغها كلها في وحدة التحكّم باستخدام الرمز البرمجي أدناه:

var items = window.performance.getEntriesByType('measure');
for (var i = 0; i < items.length; ++i) {
  var req = items[i];
  console.log('XHR ' + req.name + ' took ' + req.duration + 'ms');
}

الخاتمة

تمنحك User Timing API الكثير من الأدوات الرائعة التي يمكنك تطبيقها على أي جانب من جوانب تطبيق الويب. يمكن بسهولة تضييق نطاق نقاط الاتصال في تطبيقك من خلال رش طلبات بيانات من واجهة برمجة التطبيقات في جميع أنحاء تطبيق الويب وإجراء معالجة ما بعد بيانات التوقيت التي يتم إنشاؤها لتكوين صورة واضحة عن الوقت الذي يتم فيه قضاء الوقت. ولكن ماذا لو كان المتصفّح لا يتيح استخدام واجهة برمجة التطبيقات هذه؟ لا داعي للقلق، يمكنك العثور على علامة polyfill رائعة هنا تحاكي واجهة برمجة التطبيقات بشكل جيد ويتم تشغيلها بشكل جيد على webpagetest.org أيضًا. إذن ماذا تنتظر؟ جرِّب User Timing API في تطبيقاتك الآن، وسوف تتمكن من معرفة كيفية تسريعها وسيشكرك المستخدمون على تحسين تجربتهم بشكل كبير.