هناك الكثير من القيمة في الحصول على مقاييس تركّز على المستخدِم يمكنك قياسها بشكل عام على أيّ موقع إلكتروني معيّن. تتيح لك هذه المقاييس ما يلي:
- فهم تجربة المستخدمين الفعليين للويب ككل
- قارِن موقعك الإلكتروني بموقع إلكتروني تابع لمنافس.
- تتبُّع البيانات المفيدة والقابلة للتنفيذ في أدوات الإحصاءات بدون الحاجة إلى كتابة رمز مخصّص
توفّر المقاييس الشاملة أساسًا جيدًا، ولكن في كثير من الحالات، عليك قياس أكثر من هذه المقاييس فقط لتسجيل التجربة الكاملة لموقعك الإلكتروني المحدّد.
تتيح لك المقاييس المخصّصة قياس جوانب تجربة موقعك الإلكتروني التي قد تنطبق على موقعك فقط، مثل:
- الوقت المستغرَق لنقل تطبيق صفحة واحدة (SPA) من "صفحة" إلى أخرى
- الوقت الذي تستغرقه الصفحة لعرض البيانات التي تم جلبها من قاعدة بيانات للمستخدمين الذين سجّلوا الدخول
- المدة التي يستغرقها تطبيق معروض من جهة الخادم (SSR) لإعادة تحميل البيانات
- نسبة مرات الوصول إلى ذاكرة التخزين المؤقت للموارد التي حمّلها الزوّار المتكرّرون
- وقت استجابة الحدث لأحداث النقر أو لوحة المفاتيح في لعبة.
واجهات برمجة التطبيقات لقياس المقاييس المخصّصة
في السابق، لم يكن لدى مطوّري الويب العديد من واجهات برمجة التطبيقات ذات المستوى المنخفض لقياس الأداء، ونتيجةً لذلك، اضطروا إلى اللجوء إلى عمليات اختراق لقياس ما إذا كان الموقع الإلكتروني يؤدي أداءً جيدًا.
على سبيل المثال، من الممكن تحديد ما إذا كانت سلسلة التعليمات الرئيسية محظورة بسبب مهام JavaScript التي تستغرق وقتًا طويلاً من خلال تشغيل حلقة requestAnimationFrame
واحتساب الفرق بين كل إطار. إذا كانت المدة أطول بكثير من معدل عرض اللقطات في الشاشة، يمكنك الإبلاغ عن ذلك كمهمة طويلة. لا يُنصح باستخدام هذه الأساليب، لأنّها تؤثر في الأداء (على سبيل المثال، من خلال استنزاف البطارية).
القاعدة الأولى لقياس الأداء بفعالية هي التأكّد من أنّ تقنيات قياس الأداء لا تتسبّب في حدوث مشاكل في الأداء. لذلك، بالنسبة إلى أي مقاييس مخصّصة تقيسها على موقعك الإلكتروني، من الأفضل استخدام إحدى واجهات برمجة التطبيقات التالية إن أمكن.
Performance Observer API
واجهة برمجة التطبيقات Performance Observer API هي الآلية التي تجمع البيانات من جميع واجهات برمجة التطبيقات الأخرى المتعلّقة بالأداء والمذكورة في هذه الصفحة، وتعرضها. وفهمها أمر بالغ الأهمية للحصول على بيانات جيدة.
يمكنك استخدام PerformanceObserver
للاشتراك بشكل سلبي في الأحداث ذات الصلة بالأداء. يتيح ذلك تنشيط عمليات الاستدعاء في واجهة برمجة التطبيقات خلال فترات السكون، ما يعني أنّها لن تتداخل عادةً مع أداء الصفحة.
لإنشاء PerformanceObserver
، مرِّر دالة ردّ اتصال ليتمّ تنفيذها عند إرسال إدخالات الأداء الجديدة. بعد ذلك، يمكنك إخبار المراقب بأنواع الإدخالات التي يجب الاستماع إليها باستخدام طريقة observe()
:
const po = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
// Log the entry and all associated details.
console.log(entry.toJSON());
}
});
po.observe({type: 'some-entry-type'});
تقدّم الأقسام التالية قائمة بجميع أنواع الإدخالات المختلفة المتاحة للمراقبة، ولكن في المتصفحات الأحدث، يمكنك فحص أنواع الإدخالات المتاحة من خلال السمة الثابتة PerformanceObserver.supportedEntryTypes
.
مراقبة الإدخالات التي سبق أن حدثت
لا يمكن لعناصر PerformanceObserver
مراقبة الإدخالات إلا عند حدوثها تلقائيًا. ويمكن أن يتسبب ذلك في مشاكل إذا كنت تريد تحميل رمز إحصاءات الأداء بشكل بطيء كي لا يحظر الموارد ذات الأولوية الأعلى.
للحصول على الإدخالات السابقة (بعد حدوثها)، اضبط علامة buffered
على true
عند الاتصال بـ observe()
. سيتضمّن المتصفّح الإدخالات السابقة من مخازن إدخالات الأداء في المرة الأولى التي يتم فيها استدعاء دالة الاستدعاء PerformanceObserver
، حتى الحد الأقصى لحجم المخزن لهذا النوع.
po.observe({
type: 'some-entry-type',
buffered: true,
});
واجهات برمجة التطبيقات القديمة للأداء التي يجب تجنُّبها
قبل إتاحة Performance Observer API، كان بإمكان المطوّرين الوصول إلى إدخالات الأداء باستخدام الطرق الثلاث التالية المحدّدة في عنصر performance
:
على الرغم من أنّ واجهات برمجة التطبيقات هذه لا تزال متاحة، لا يُنصح باستخدامها لأنّها لا تتيح لك الاستماع إلى وقت نشر الإدخالات الجديدة. بالإضافة إلى ذلك، لا يتم عرض العديد من واجهات برمجة التطبيقات الجديدة (مثل largest-contentful-paint
) من خلال عنصر performance
، بل يتم عرضها من خلال PerformanceObserver
فقط.
ما لم تكن بحاجة إلى التوافق مع Internet Explorer على وجه التحديد، من الأفضل تجنُّب هذه الطرق في الرمز البرمجي واستخدام PerformanceObserver
من الآن فصاعدًا.
User Timing API
User Timing API هي واجهة برمجة تطبيقات لقياس الأداء العام المقاييس المستندة إلى الوقت. تتيح لك هذه الطريقة وضع علامات عشوائية على نقاط في الوقت، ثم قياس المدة بين تلك العلامات لاحقًا.
// Record the time immediately before running a task.
performance.mark('myTask:start');
await doMyTask();
// Record the time immediately after running a task.
performance.mark('myTask:end');
// Measure the delta between the start and end of the task
performance.measure('myTask', 'myTask:start', 'myTask:end');
على الرغم من أنّ واجهات برمجة التطبيقات مثل Date.now()
أو performance.now()
تمنحك إمكانات مشابهة، فإنّ ميزة استخدام User Timing API هي أنّها تتكامل بشكل جيد مع أدوات الأداء. على سبيل المثال، تعرِض "أدوات مطوّري البرامج" في Chrome قياسات "مُدد استجابة المستخدِم" في لوحة "الأداء"، وسيتتبّع العديد من مقدّمي خدمات الإحصاءات أيضًا تلقائيًا أي قياسات تجريها ويرسلون بيانات المدة إلى الخلفية في خدمة الإحصاءات.
للإبلاغ عن قياسات "وقت المستخدِم"، يمكنك استخدام PerformanceObserver والتسجيل لمراقبة الإدخالات من النوع measure
:
// Create the performance observer.
const po = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
// Log the entry and all associated details.
console.log(entry.toJSON());
}
});
// Start listening for `measure` entries to be dispatched.
po.observe({type: 'measure', buffered: true});
Long Tasks API
تُعدّ واجهة برمجة التطبيقات Long Tasks API مفيدة لمعرفة الحالات التي يتم فيها حظر سلسلة التعليمات الرئيسية للمتصفّح لفترة كافية للتأثير في معدّل عرض اللقطات أو وقت استجابة الإدخال. ستحدّد واجهة برمجة التطبيقات أي مهام تستغرق أكثر من 50 ملي ثانية لتنفيذها.
في أي وقت تحتاج فيه إلى تنفيذ رمز برمجي مُكلّف أو تحميل نصوص برمجية كبيرة وتنفيذها، من المفيد تتبُّع ما إذا كان هذا الرمز البرمجي يحظر سلسلة التعليمات الرئيسية. في الواقع، يتم إنشاء العديد من المقاييس ذات المستوى الأعلى استنادًا إلى Long Tasks API نفسها (مثل وقت التفاعل (TTI) وإجمالي وقت الحظر (TBT)).
لتحديد وقت حدوث المهام الطويلة، يمكنك استخدام PerformanceObserver والتسجيل لمراقبة الإدخالات من النوع longtask
:
// Create the performance observer.
const po = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
// Log the entry and all associated details.
console.log(entry.toJSON());
}
});
// Start listening for `longtask` entries to be dispatched.
po.observe({type: 'longtask', buffered: true});
Long Animation Frames API
Long Animation Frames API هي إصدار جديد من Long Tasks API التي تفحص اللقطات الطويلة التي تزيد مدتها عن 50 ملي ثانية بدلاً من المهام الطويلة. يعالج ذلك بعض المشاكل في Long Tasks API، بما في ذلك تحديد المصدر بشكل أفضل ونطاق أوسع من التأخيرات التي قد تؤدي إلى حدوث مشاكل.
لتحديد وقت حدوث اللقطات الطويلة، يمكنك استخدام PerformanceObserver والتسجيل لمراقبة الإدخالات من النوع long-animation-frame
:
// Create the performance observer.
const po = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
// Log the entry and all associated details.
console.log(entry.toJSON());
}
});
// Start listening for `long-animation-frame` entries to be dispatched.
po.observe({type: 'long-animation-frame', buffered: true});
Element Timing API
يُعدّ مقياس سرعة عرض أكبر محتوى مرئي (LCP) مفيدًا لمعرفة وقت ظهور أكبر صورة أو مقطع نصي على الشاشة، ولكن في بعض الحالات، قد تحتاج إلى قياس وقت عرض عنصر مختلف.
في هذه الحالات، استخدِم Element Timing API. تم إنشاء LCP API في الواقع على Element Timing API وتضيف تقارير تلقائية عن أكبر عنصر يتضمّن محتوى، ولكن يمكنك أيضًا إعداد تقارير عن العناصر الأخرى من خلال إضافة سمة elementtiming
إليها صراحةً، وتسجيل PerformanceObserver لمراقبة نوع الإدخال element
.
<img elementtiming="hero-image" />
<p elementtiming="important-paragraph">This is text I care about.</p>
<!-- ... -->
<script>
const po = new PerformanceObserver((entryList) => {
for (const entry of entryList.getEntries()) {
// Log the entry and all associated details.
console.log(entry.toJSON());
}
});
// Start listening for `element` entries to be dispatched.
po.observe({type: 'element', buffered: true});
</script>
Event Timing API
يقيّم مقياس مدى استجابة الصفحة لتفاعلات المستخدم (INP) مدى استجابة الصفحة بشكل عام من خلال رصد جميع التفاعلات المتعلقة بالنقر وبلوحة المفاتيح طوال الفترة التي يزور فيها المستخدم الصفحة. غالبًا ما يكون مقياس INP للصفحة هو التفاعل الذي استغرق أطول وقت لإكماله، وذلك من وقت بدء المستخدم التفاعل إلى وقت رسم المتصفّح للإطار التالي الذي يعرض النتيجة المرئية للبيانات التي أدخلها المستخدم.
يمكن استخدام مقياس INP من خلال Event Timing API. تعرض واجهة برمجة التطبيقات هذه عددًا من الطوابع الزمنية التي تحدث خلال دورة حياة الحدث، بما في ذلك:
-
startTime
: الوقت الذي يتلقّى فيه المتصفّح الحدث. -
processingStart
: الوقت الذي يتمكّن فيه المتصفّح من بدء معالجة معالِجات الأحداث للحدث. processingEnd
: الوقت الذي ينتهي فيه المتصفّح من تنفيذ كل الرمز البرمجي المتزامن الذي بدأ من معالجات الأحداث لهذا الحدث.-
duration
: الوقت (المُقرَّب إلى 8 مللي ثانية لأسباب تتعلق بالأمان) بين وقت استلام المتصفّح للحدث ووقت تمكّنه من رسم اللقطة التالية بعد الانتهاء من تنفيذ كل الرموز البرمجية المتزامنة التي تم بدءها من معالجات الأحداث
يوضّح المثال التالي كيفية استخدام هذه القيم لإنشاء قياسات مخصّصة:
const po = new PerformanceObserver((entryList) => {
// Get the last interaction observed:
const entries = Array.from(entryList.getEntries()).forEach((entry) => {
// Get various bits of interaction data:
const inputDelay = entry.processingStart - entry.startTime;
const processingTime = entry.processingEnd - entry.processingStart;
const presentationDelay = entry.startTime + entry.duration - entry.processingEnd;
const duration = entry.duration;
const eventType = entry.name;
const target = entry.target || "(not set)"
console.log("----- INTERACTION -----");
console.log(`Input delay (ms): ${inputDelay}`);
console.log(`Event handler processing time (ms): ${processingTime}`);
console.log(`Presentation delay (ms): ${presentationDelay}`);
console.log(`Total event duration (ms): ${duration}`);
console.log(`Event type: ${eventType}`);
console.log(target);
});
});
// A durationThreshold of 16ms is necessary to include more
// interactions, since the default is 104ms. The minimum
// durationThreshold is 16ms.
po.observe({type: 'event', buffered: true, durationThreshold: 16});
Resource Timing API
توفّر واجهة برمجة التطبيقات Resource Timing API للمطوّرين إحصاءات تفصيلية حول كيفية تحميل موارد صفحة معيّنة. على الرغم من اسم واجهة برمجة التطبيقات، لا تقتصر المعلومات التي تقدّمها على بيانات التوقيت فقط (على الرغم من توفّر الكثير من هذه البيانات). تشمل البيانات الأخرى التي يمكنك الوصول إليها ما يلي:
-
initiatorType
: كيفية جلب المورد: مثل من علامة<script>
أو<link>
أو من طلبfetch()
-
nextHopProtocol
: البروتوكول المستخدَم لاسترداد المرجع، مثلh2
أوquic
. -
encodedBodySize
/decodedBodySize]: حجم المورد بترميزه أو بدون ترميزه (على التوالي) transferSize
: حجم المورد الذي تم نقله فعليًا عبر الشبكة. عندما توفّر ذاكرة التخزين المؤقت الموارد، يمكن أن تكون هذه القيمة أصغر بكثير منencodedBodySize
، وفي بعض الحالات يمكن أن تكون صفرًا (إذا لم تكن هناك حاجة إلى إعادة التحقّق من صحة ذاكرة التخزين المؤقت).
يمكنك استخدام السمة transferSize
لإدخالات توقيت الموارد لقياس مقياس نسبة مرات الوصول إلى ذاكرة التخزين المؤقت أو مقياس إجمالي حجم الموارد المخزّنة مؤقتًا، ما قد يكون مفيدًا في فهم مدى تأثير استراتيجية تخزين الموارد مؤقتًا في أداء الزوّار المتكرّرين.
يسجِّل المثال التالي جميع الموارد التي طلبتها الصفحة ويشير إلى ما إذا كانت ذاكرة التخزين المؤقت قد توفّرت لكل مورد أم لا.
// Create the performance observer.
const po = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
// If transferSize is 0, the resource was fulfilled using the cache.
console.log(entry.name, entry.transferSize === 0);
}
});
// Start listening for `resource` entries to be dispatched.
po.observe({type: 'resource', buffered: true});
Navigation Timing API
تتشابه Navigation Timing API مع Resource Timing API، ولكنها لا تسجّل سوى طلبات التنقّل. يشبه نوع الإدخال navigation
أيضًا نوع الإدخال resource
، ولكنه يحتوي على بعض المعلومات الإضافية الخاصة بطلبات التنقّل فقط (مثل وقت بدء حدثَي DOMContentLoaded
وload
).
يتوفّر مقياس واحد يتتبّعه العديد من المطوّرين لفهم وقت استجابة الخادم (وقت وصول أول بايت (TTFB)) باستخدام واجهة برمجة التطبيقات Navigation Timing API، وهو الطابع الزمني responseStart
للسجلّ على وجه التحديد.
// Create the performance observer.
const po = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
// If transferSize is 0, the resource was fulfilled using the cache.
console.log('Time to first byte', entry.responseStart);
}
});
// Start listening for `navigation` entries to be dispatched.
po.observe({type: 'navigation', buffered: true});
من المقاييس الأخرى التي قد يهتم بها المطوّرون الذين يستخدمون مشغّل الخدمات هي وقت بدء تشغيل مشغّل الخدمات لطلبات التنقّل. هذا هو الوقت الذي يستغرقه المتصفّح لبدء سلسلة مهام عامل الخدمة قبل أن يتمكّن من بدء اعتراض أحداث الجلب.
يمكن تحديد وقت بدء تشغيل الخدمة لطلب تنقّل معيّن من خلال الفرق بين entry.responseStart
وentry.workerStart
.
// Create the performance observer.
const po = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
console.log('Service Worker startup time:',
entry.responseStart - entry.workerStart);
}
});
// Start listening for `navigation` entries to be dispatched.
po.observe({type: 'navigation', buffered: true});
Server Timing API
تتيح لك واجهة برمجة التطبيقات Server Timing API تمرير بيانات التوقيت الخاصة بالطلب من خادمك إلى المتصفّح من خلال رؤوس الاستجابة. على سبيل المثال، يمكنك الإشارة إلى الوقت الذي استغرقته عملية البحث عن البيانات في قاعدة بيانات لطلب معيّن، ما قد يكون مفيدًا في تصحيح أخطاء مشاكل الأداء الناتجة عن بطء الخادم.
بالنسبة إلى المطوّرين الذين يستخدِمون مزوّدي خدمات إحصاءات خارجيين، فإنّ Server Timing API هي الطريقة الوحيدة لربط بيانات أداء الخادم بمقاييس النشاط التجاري الأخرى التي قد تقيسها أدوات الإحصاءات هذه.
لتحديد بيانات توقيت الخادم في ردودك، يمكنك استخدام عنوان الاستجابة Server-Timing
. في ما يلي مثال:
HTTP/1.1 200 OK
Server-Timing: miss, db;dur=53, app;dur=47.2
بعد ذلك، يمكنك من صفحاتك قراءة هذه البيانات في كلّ من إدخالات resource
أو navigation
من واجهات برمجة التطبيقات Resource Timing وNavigation Timing API.
// Create the performance observer.
const po = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
// Logs all server timing data for this response
console.log('Server Timing', entry.serverTiming);
}
});
// Start listening for `navigation` entries to be dispatched.
po.observe({type: 'navigation', buffered: true});