فهم تطبيق الويب
تُعدّ تطبيقات الويب العالية الأداء ضرورية لتقديم تجربة رائعة للمستخدم. نظرًا لأن تطبيقات الويب تصبح أكثر تعقيدًا، أصبح فهم تأثير الأداء أمرًا حيويًا لإنشاء تجربة مقنعة. على مدار السنوات القليلة الماضية، ظهر عدد من واجهات برمجة التطبيقات المختلفة في المتصفح للمساعدة في تحليل أداء الشبكة وأوقات التحميل وما إلى ذلك، غير أن هذه الواجهات لا توفر بالضرورة تفاصيل دقيقة ومرونة كافية لاكتشاف العوامل التي تبطئ تطبيقك. أدخِل 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. هذا النوع هو قيمة النقطة العائمة التي تعرض أيضًا الوقت بالمللي ثانية. ولكن وبما أنها نقطة عائمة، يمكن أن تمثل القيمة أجزاءً من المللي ثانية، وبالتالي يمكن أن ينتج عنها دقة تبلغ ألف من المللي ثانية.
واجهة User Timing
والآن، بعد أن أصبح لدينا طوابع زمنية عالية الدقة، لنستخدم واجهة User Timing لسحب معلومات التوقيت.
توفر واجهة المستخدم 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.peformance.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_full_loading" في السمة 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 في تطبيقاتك الآن، وستتعرف على كيفية تسريعها، وسيشكرك المستخدمون على تحسين تجربتهم بشكل كبير.