كيفية تقييم أداء التحميل في الميدان باستخدام توقيت التنقّل وتوقيت الموارد

تعرَّف على أساسيات استخدام واجهات برمجة التطبيقات Navigation وResource Timing لتقييم أداء التحميل في الميدان.

تاريخ النشر: 8 تشرين الأول (أكتوبر) 2021

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

إنّ الاختبار الاصطناعي ليس سيئًا بطبيعته، ولكنه لا يمثّل سرعة تحميل موقعك الإلكتروني للمستخدمين الفعليين. يتطلّب ذلك بيانات ميدانية يمكنك جمعها من واجهتَي برمجة التطبيقات Navigation Timing وResource Timing API.

Navigation Timing وResource Timing هما وجهتان مشابهتان لواجهة برمجة التطبيقات تتداخلان بشكل كبير وتقيسان شيئَين مختلفَين:

  • يقيس توقيت التنقّل سرعة طلبات مستندات HTML (أي طلبات التنقّل).
  • تقيس مُدد تحميل الموارد سرعة طلبات الموارد التي تعتمد على المستندات، مثل ملفات CSS وJavaScript والصور وأنواع الموارد الأخرى.

تعرض واجهات برمجة التطبيقات هذه بياناتها في مخازن مؤقتة لإدخالات الأداء، ويمكن الوصول إليها في المتصفّح باستخدام JavaScript. هناك طرق متعدّدة لاسترداد بيانات من مخزن مؤقت للأداء، ولكن الطريقة الشائعة هي استخدام performance.getEntriesByType:

// Get Navigation Timing entries:
performance.getEntriesByType('navigation');

// Get Resource Timing entries:
performance.getEntriesByType('resource');

يقبل performance.getEntriesByType سلسلة تصف نوع الإدخالات التي تريد استردادها من المخزن المؤقت لإدخال الأداء. تستردّ 'navigation' و'resource' أوقات استجابة واجهتَي برمجة التطبيقات Navigation Timing وResource Timing API، على التوالي.

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

عمر طلب الشبكة وتوقيته

إنّ جمع أوقات التنقّل والموارد وتحليلها يشبه إلى حدٍّ ما علم الآثار، لأنّك تعيد إنشاء الحياة القصيرة لطلب الشبكة بعد حدوثها. من المفيد أحيانًا الاطّلاع على المفاهيم بشكل مرئي، ويمكن أن تساعدك أدوات المطوّرين في متصفحك في ما يتعلّق بطلبات الشبكة.

مُدد الاتصال بالشبكة كما هو موضّح في "أدوات مطوّري البرامج" في Chrome إنّ التوقيتات الموضّحة هي لإضافة الطلبات إلى "قائمة الانتظار"، وتفاوض الاتصال، والطلب نفسه، والاستجابة في أشرطة مُرمّزة بالألوان.
عرض مرئي لطلب شبكة في لوحة الشبكة في "أدوات مطوري البرامج في Chrome"

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

بحث نظام أسماء النطاقات

عندما ينتقل المستخدم إلى عنوان URL، يتم الاستعلام عن نظام أسماء النطاقات (DNS) لترجمة أحد المجالات إلى عنوان IP. قد تستغرق هذه العملية وقتًا كبيرًا - وقت ستحتاج فيه إلى قياسه في المجال، حتى. يعرض وقت التنقل وتوقيت الموارد توقيتين مرتبطين بنظام أسماء النطاقات:

  • domainLookupStart هو وقت بدء البحث في نظام أسماء النطاقات.
  • domainLookupEnd هو وقت انتهاء عملية البحث في نظام أسماء النطاقات.

يمكن احتساب إجمالي وقت البحث في نظام أسماء النطاقات من خلال طرح مقياس البدء من مقياس النهاية:

// Measuring DNS lookup time
const [pageNav] = performance.getEntriesByType('navigation');
const totalLookupTime = pageNav.domainLookupEnd - pageNav.domainLookupStart;

التفاوض على الاتصال

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

  • connectStart هو عندما يبدأ المتصفّح في فتح اتصال بخادم ويب.
  • يشير الرمز secureConnectionStart إلى وقت بدء العميل لتفاوض بروتوكول أمان طبقة النقل (TLS).
  • connectEnd هو رمز الاتصال بخادم الويب.

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

// Quantifying total connection time
const [pageNav] = performance.getEntriesByType('navigation');
const connectionTime = pageNav.connectEnd - pageNav.connectStart;
let tlsTime = 0; // <-- Assume 0 to start with

// Was there TLS negotiation?
if (pageNav.secureConnectionStart > 0) {
  // Awesome! Calculate it!
  tlsTime = pageNav.connectEnd - pageNav.secureConnectionStart;
}

بعد انتهاء بحث نظام أسماء النطاقات والتفاوض على الاتصال، يتم تفعيل التوقيتات المتعلقة بجلب المستندات والموارد التابعة لها.

الطلبات والردود

يتأثر أداء التحميل بنوعَين من العوامل:

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

ويؤثر كلا النوعَين من العوامل في أداء التحميل. تعتبر التوقيتات المتعلقة بهذه العوامل أمرًا حيويًا، لأنها تصف المدة التي تستغرقها الموارد لتنزيلها. يصف كل من وقت التنقل وتوقيت المورد أداء التحميل باستخدام المقاييس التالية:

  • يشير الرمز fetchStart إلى وقت بدء المتصفّح في جلب مورد (توقيت الموارد) أو مستند لطلب تنقّل (توقيت التنقّل). ويسبق ذلك الطلب الفعلي، وهو النقطة التي يتحقّق فيها المتصفّح من ذاكرات التخزين المؤقت (على سبيل المثال، مثيلا HTTP وCache).
  • يشير الرمز workerStart إلى وقت بدء معالجة طلب ضمن معالِج أحداث fetch لعامل الخدمة. سيكون هذا العنصر 0 عندما لا يتحكّم أي مشغّل خدمات في الصفحة الحالية.
  • requestStart هو وقت تقديم المتصفّح للطلب.
  • responseStart هو عند وصول البايت الأول من الردّ.
  • responseEnd هو وقت وصول البايت الأخير من الاستجابة.

تسمح لك هذه التوقيتات بقياس جوانب متعدّدة من أداء التحميل، مثل البحث في ذاكرة التخزين المؤقت ضمن مشغّل الخدمات ووقت التنزيل:

// Cache seek plus response time of the current document
const [pageNav] = performance.getEntriesByType('navigation');
const fetchTime = pageNav.responseEnd - pageNav.fetchStart;

// Service worker time plus response time
let workerTime = 0;

if (pageNav.workerStart > 0) {
  workerTime = pageNav.responseEnd - pageNav.workerStart;
}

يمكنك أيضًا قياس جوانب أخرى من وقت استجابة الطلب والاستجابة:

const [pageNav] = performance.getEntriesByType('navigation');

// Request time only (excluding redirects, DNS, and connection/TLS time)
const requestTime = pageNav.responseStart - pageNav.requestStart;

// Response time only (download)
const responseTime = pageNav.responseEnd - pageNav.responseStart;

// Request + response time
const requestResponseTime = pageNav.responseEnd - pageNav.requestStart;

القياسات الأخرى التي يمكنك إجراؤها

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

  • عمليات إعادة توجيه الصفحات: تعد عمليات إعادة التوجيه مصدرًا مهمًا لوقت الاستجابة الإضافي، ولا سيما سلاسل عمليات إعادة التوجيه. تتم إضافة وقت الاستجابة بعدة طرق، مثل القفزات من HTTP إلى HTTP، وكذلك عمليات إعادة التوجيه 301/غير المخزّنة مؤقتًا. إنّ التوقيتات redirectStart وredirectEnd وredirectCount مفيدة في تقييم وقت استجابة إعادة التوجيه.
  • توقُّف تحميل المستند: في الصفحات التي تُشغِّل رمزًا في معالج أحداث unload، على المتصفّح تنفيذ هذا الرمز قبل أن يتمكّن من الانتقال إلى الصفحة التالية. يقيس unloadEventStart وunloadEventEnd تفريغ المستندات.
  • معالجة المستندات: قد لا يكون وقت معالجة المستندات ذا تأثير كبير ما لم يرسل موقعك الإلكتروني حِزم بيانات HTML كبيرة جدًا. إذا كان هذا الوصف يناسب حالتك، قد تهمّك أوقات domInteractive وdomContentLoadedEventStart وdomContentLoadedEventEnd وdomComplete.

كيفية الحصول على التوقيتات في الرمز

تستخدم جميع الأمثلة التي تم عرضها حتى الآن performance.getEntriesByType، ولكن هناك طرق أخرى لطلب بيانات من ذاكرة التخزين المؤقت لإدخالات الأداء، مثل performance.getEntriesByName وperformance.getEntries. هذه الطرق جيدة عند الحاجة فقط إلى تحليل الضوء. ومع ذلك، في حالات أخرى، قد تستخدم هذه الرموز في تنفيذ سلسلة التعليمات الرئيسية بشكل زائد عن طريق تكرار عدد كبير من الإدخالات أو حتى البحث بشكل متكرر عن المخزن المؤقت للأداء للعثور على إدخالات جديدة.

والأسلوب المُقترَح لجمع الإدخالات من المخزن المؤقت لإدخال الأداء هو استخدام PerformanceObserver. ينتظر PerformanceObserver إدخالات الأداء ويوفرها عند إضافتها إلى المخزن المؤقت:

// Create the performance observer:
const perfObserver = new PerformanceObserver((observedEntries) => {
  // Get all resource entries collected so far:
  const entries = observedEntries.getEntries();

  // Iterate over entries:
  for (let i = 0; i < entries.length; i++) {
    // Do the work!
  }
});

// Run the observer for Navigation Timing entries:
perfObserver.observe({
  type: 'navigation',
  buffered: true
});

// Run the observer for Resource Timing entries:
perfObserver.observe({
  type: 'resource',
  buffered: true
});

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

كيفية الاتصال بالمنزل

بعد جمع كلّ الأوقات التي تحتاجها، يمكنك إرسالها إلى نقطة نهاية لإجراء المزيد من التحليل. يمكنك إجراء ذلك بطريقتَين: باستخدام navigator.sendBeacon أو fetch مع ضبط الخيار keepalive. ستُرسِل كلتا الطريقتَين طلبًا إلى نقطة نهاية محدّدة بطريقة غير حظر، وسيتم وضع الطلب في قائمة الانتظار بطريقة تتجاوز جلسة الصفحة الحالية إذا لزم الأمر:

// Check for navigator.sendBeacon support:
if ('sendBeacon' in navigator) {
  // Caution: If you have lots of performance entries, don't
  // do this. This is an example for illustrative purposes.
  const data = JSON.stringify(performance.getEntries());

  // Send the data!
  navigator.sendBeacon('/analytics', data);
}

في هذا المثال، ستصل سلسلة JSON في حمولة POST يمكنك فك تشفيرها ومعالجتها وتخزينها في الخلفية في التطبيق حسب الحاجة.

الخاتمة

بعد جمع المقاييس، عليك تحديد كيفية تحليل بيانات الحقل هذه. عند تحليل بيانات الحقل، هناك بعض القواعد العامة التي يجب اتّباعها لضمان استخلاص نتائج مفيدة:

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

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

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