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

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

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، يمكننا استخدامها لتحليل الوقت الذي تستغرقه جميع طلبات XMLHttpRequests في تطبيق الويب.

سنعدّل أولاً جميع طلبات 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 في تطبيقاتك الآن، وسوف تتمكن من معرفة كيفية تسريعها وسيشكرك المستخدمون على تحسين تجربتهم بشكل كبير.