فهم تطبيق الويب
تطبيقات الويب العالية الأداء ضرورية لتوفير تجربة رائعة للمستخدم. مع تزايد تعقيد تطبيقات الويب، يصبح فهم تأثير الأداء أمرًا حيويًا لإنشاء تجربة جذابة. على مدار السنوات القليلة الماضية، ظهر عدد من واجهات برمجة التطبيقات المختلفة في المتصفّح للمساعدة في تحليل أداء الشبكة وأوقات التحميل وما إلى ذلك، ولكنّها لا تقدّم بالضرورة تفاصيل دقيقة مع مرونة كافية لمعرفة ما يؤدي إلى إبطاء تطبيقك. أدخِل User Timing API التي توفّر آلية يمكنك استخدامها لتجهيز تطبيق الويب الخاص بك لتحديد الأماكن التي يقضي فيها تطبيقك وقته. في هذه المقالة، سنتناول واجهة برمجة التطبيقات بالإضافة إلى أمثلة على كيفية استخدامها.
لا يمكنك تحسين ما لا يمكنك قياسه
الخطوة الأولى لتسريع تطبيق ويب بطيء هي تحديد الأجزاء التي تستغرق وقتًا طويلاً. إنّ قياس تأثير الوقت في أجزاء من رمز JavaScript هو الطريقة المثالية لتحديد النقاط الفعّالة، وهي الخطوة الأولى في العثور على كيفية تحسين الأداء. لحسن الحظ، توفّر واجهة برمجة التطبيقات User Timing طريقة يمكنك من خلالها إدراج طلبات البيانات من واجهة برمجة التطبيقات في أجزاء مختلفة من Javascript ثم استخراج بيانات تفصيلية عن التوقيت يمكن استخدامها لمساعدتك في تحسين الأداء.
وقت بدقة عالية وnow()
إنّ الدقة هي جزء أساسي من قياس الوقت بدقة. في السابق، كان لدينا توقيت يعتمد على قياس المللي ثانية، وهو أمر جيد، ولكن إنشاء موقع إلكتروني بمعدل 60 لقطة في الثانية بدون أي تقطّع يعني أنّه يجب رسم كل لقطة في 16 ملي ثانية. لذلك، عندما تكون الدقة بالمللي ثانية فقط، فإنّها تفتقر إلى الدقة اللازمة لإجراء تحليل جيد. أدخِل High Resolution Time، وهو نوع توقيت جديد مدمج في المتصفّحات الحديثة. يمنحنا High Resolution Time طوابع زمنية ذات نقطة عائمة يمكن أن تكون دقيقة بدقة ميكروثانية - أفضل بألف مرة من ذي قبل.
للحصول على الوقت الحالي في تطبيق الويب، يمكنك استدعاء الطريقة now() التي تشكّل إضافة لواجهة الأداء. يوضّح الرمز التالي كيفية إجراء ذلك:
var myTime = window.performance.now();
هناك واجهة أخرى تُسمى PerformanceTiming توفّر عددًا من الأوقات المختلفة المرتبطة بكيفية تحميل تطبيق الويب. تعرض الطريقة now() الوقت المنقضي منذ حدوث الوقت navigationStart في PerformanceTiming.
نوع DOMHighResTimeStamp
في السابق، كان يتم استخدام Date.now() لقياس الوقت في تطبيقات الويب، وهو يعرض DOMTimeStamp. تعرض DOMTimeStamp عددًا صحيحًا من الملّي ثانية كقيمة لها. In order to provide the higher accuracy needed for High Resolution time, a new type called DOMHighResTimeStamp was introduced. هذا النوع هو قيمة نقطة عائمة تُرجع أيضًا الوقت بالمللي ثانية. ولكن بما أنّها قيمة عائمة، يمكن أن تمثّل أجزاء من الملي ثانية، وبالتالي يمكن أن تصل دقتها إلى جزء من الألف من الملي ثانية.
واجهة User Timing
والآن بعد أن أصبح لدينا طوابع زمنية عالية الدقة، لنستخدم واجهة أوقات المستخدم لاستخراج معلومات التوقيت.
توفّر واجهة User Timing دوال تتيح لنا استدعاء طرق في أماكن مختلفة في تطبيقنا يمكن أن توفّر مسار تنقّل على غرار هانسل وغريتل لتتبُّع الوقت الذي يتم قضاؤه.
جارٍ استخدام 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 في تطبيقاتك الآن، وستكتشف كيفية تسريعها وسيشكرك المستخدمون على تحسين تجربتهم بشكل كبير.