قياس مقاييس الأداء المهمة باستخدام "إحصاءات Google"

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

تُعدّ أدوات مثل WebPagetest.org بداية رائعة لتحسين الأداء، ولكنّ الاختبار الحقيقي لأداء الموقع الإلكتروني سيكون دائمًا البيانات الفعلية التي يتم جمعها من المستخدمين.

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

أهداف الدورة التعليمية

  • كيفية قياس مقاييس الأداء بدقة وفعالية باستخدام واجهة برمجة التطبيقات User Timings API
  • كيفية إرسال هذه البيانات إلى "إحصاءات Google" لكي يتم دمجها في تقاريرك

المتطلبات

  • متصفّح يتضمّن وحدة تحكّم المطوّرين
  • Web Server for Chrome، أو استخدام خادم الويب الذي تختاره
  • الرمز النموذجي
  • محرّر نصوص
  • (اختياري) حساب على "إحصاءات Google"

كيف ستستخدم هذا البرنامج التعليمي؟

قراءة المحتوى فقط قراءة المحتوى وإكمال التمارين

كيف تقيّم تجربتك في إنشاء مواقع إلكترونية أو تطبيقات؟

مبتدئ متوسط متقدّم

يمكنك إما تنزيل كل الرمز النموذجي إلى جهاز الكمبيوتر...

تنزيل ملف Zip

...أو استنسِخ مستودع GitHub من سطر الأوامر.

git clone https://github.com/googlecodelabs/performance-analytics.git

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

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

في هذا الدرس العملي، ستستخدم ملف HTML واحدًا يحمّل الأصول التالية:

  • خطوط الويب
  • أوراق الأنماط
  • الصور
  • JavaScript

وستكتب رمزًا برمجيًا جديدًا يقيس مقاييس الأداء الرئيسية لكل نوع من أنواع مواد العرض هذه.

اعتبارات أداء مواد العرض

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

CSS

على سبيل المثال، تمنع أوراق الأنماط عرض جميع العناصر في نموذج المستند (DOM) التي تليها، ما يعني أنّ المتصفّح عليه إرسال طلب للحصول على ورقة الأنماط وتنزيلها وتحليلها قبل أن يتمكّن من عرض أي محتوى في نموذج المستند (DOM) يليها. لهذا السبب، من الأفضل عادةً وضع أوراق الأنماط في <head> المستند. وبسبب طبيعة CSS الحظرية، يُنصح أيضًا في كثير من الأحيان بوضع CSS المهم فقط في <head> وتحميل CSS غير المهم بشكل غير متزامن بعد ذلك.

JavaScript

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

لهذا السبب، يُنصح غالبًا بتحميل JavaScript قبل علامة الإغلاق </body> مباشرةً، حتى يكون معظم نموذج DOM متاحًا في أسرع وقت ممكن.

خطوط الويب

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

الصور

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

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

لعرض العرض التوضيحي، أنشئ مجلدًا جديدًا وأضِف ملفًا باسم index.html بداخله. بعد ذلك، انسخ الرمز أدناه وألصِقه في ملف index.html.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Performance Analytics Demo</title>

  <!-- Start fonts -->
  <link href="https://fonts.googleapis.com/css?family=Roboto:400,700,400italic" rel="stylesheet">
  <!-- End fonts -->

  <!-- Start CSS -->
  <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet">
  <style>
    body { font-family: Roboto, sans-serif; margin: 1em; }
    img { float: left; height: auto; width: 33.33%; }
    .gallery { overflow: hidden; }
  </style>
  <!-- End CSS -->

</head>
<body>

  <div class="container">

    <!-- Start images -->
    <div class="gallery">
      <img src="http://lorempixel.com/380/200/animals/1/">
      <img src="http://lorempixel.com/380/200/animals/2/">
      <img src="http://lorempixel.com/380/200/animals/3/">
    </div>
    <!-- End images -->

    <h1>Performance Analytics Demo</h1>
    <p>Real performance data from real users.</p>

  </div>

  <!-- Start JavaScript -->
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
  <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
  <!-- End JavaScript -->

</body>
</html>

بعد ذلك، افتح تطبيق Web Server for Chrome وابدأ تشغيل خادم محلي في الدليل الذي أنشأته للتو. تأكَّد من وضع علامة في المربّع "عرض index.html تلقائيًا".

Screen Shot 2016-05-11 at 1.03.43 PM.png

من المفترض أن تتمكّن الآن من الانتقال إلى http://127.0.0.1:8887/ في متصفّحك والاطّلاع على الملف التجريبي. من المفترض أن تظهر بشكلٍ مشابه لما يلي:

Screen Shot 2016-05-11 at 10.59.03 AM.png

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

كما ذكرنا في قسم اعتبارات أداء مواد العرض سابقًا، تحظر صفحات CSS عرض عناصر DOM بالإضافة إلى تنفيذ النصوص البرمجية التي تليها في DOM.

يحتوي ملف العرض التوضيحي الذي أنشأته للتو على CSS التالي الذي يشير إلى Bootstrap وبعض الأنماط المضمّنة.

<!-- Start CSS -->
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet">
<style>
  body { font-family: Roboto, sans-serif; margin: 1em; }
  img { float: left; height: auto; width: 33.33%; }
  .gallery { overflow: hidden; }
</style>
<!-- End CSS -->

بما أنّ CSS يحظر عرض عناصر DOM وتنفيذ النصوص البرمجية، من الممكن تحديد وقت انتهاء حظر CSS من خلال إضافة علامة <script> مباشرةً بعد CSS الذي يخزّن الوقت الحالي.

يمكنك إجراء ذلك من خلال إنشاء متغيّر وتعيين new Date() له، ولكن بفضل User Timings API، هناك طريقة أسهل بكثير: طريقة performance.mark.

لتحديد الوقت الذي تنتهي فيه CSS من حظر كلّ من العرض وتنفيذ النصوص البرمجية، أضِف سطر الرمز التالي قبل تعليق الإغلاق <!-- End CSS --> مباشرةً.

<script>performance.mark('css:unblock');</script>

تنشئ الطريقة performance.mark طابعًا زمنيًا عالي الدقة في هذه النقطة الزمنية المحدّدة، وتربطه بأي اسم تم تمريره إلى الطريقة. في هذه الحالة، سمّيت العلامة "css:unblock".

تتكامل طريقة performance.mark مع طريقة performance.measure، التي تُستخدَم لحساب فرق الوقت بين علامتَين (بالإضافة إلى العلامات التي تضعها، يمكنك أيضًا استخدام العلامات التي يضعها المتصفّح تلقائيًا لمختلف النقاط في Navigation Timing API).

تقيس دالة الأداة المساعدة التالية مدة الوقت بين علامة أضفتها وعلامة responseEnd التي أنشأتها Navigation Timing API وتعرضها.

function measureDuration(mark, opt_reference) {
  var reference = opt_reference || 'responseEnd';
  var name = reference + ':' + mark;

  // Clears any existing measurements with the same name.
  performance.clearMeasures(name);

  // Creates a new measurement from the reference point to the specified mark.
  // If more than one mark with this name exists, the most recent one is used.
  performance.measure(name, reference, mark);

  // Gets the value of the measurement just created.
  var measure = performance.getEntriesByName(name)[0];

  // Returns the measure duration.
  return measure.duration;
}

لبدء استخدام دالة الأداة المساعدة هذه، أنشئ ملفًا جديدًا باسم perf-analytics.js (في الدليل نفسه الذي يوجد فيه الملف index.html) وانسخ الرمز أعلاه والصقه فيه.

بعد تحديد هذه الدالة، يمكنك استدعاؤها وإدخال اسم العلامة "css:unblock". لتجنُّب التداخل مع تحميل أي موارد أخرى، عليك تأجيل تنفيذ هذه القياسات إلى ما بعد تشغيل حدث تحميل النافذة.

بعد كتابة دالة لاستدعاء هذه الرمز، من المفترض أن يبدو ملف perf-analytics.js على النحو التالي:

window.onload = function() {
  measureCssUnblockTime();
};


/**
 * Calculates the time duration between the responseEnd timing event and when
 * the CSS stops blocking rendering, then logs that value to the console.
 */
function measureCssUnblockTime() {
  console.log('CSS', 'unblock', measureDuration('css:unblock'));
}


/**
 * Accepts a mark name and an optional reference point in the navigation timing
 * API and returns the time duration between the reference point and the last
 * mark (chronologically).
 * @param {string} mark The mark name.
 * @param {string=} opt_reference An optional reference point from the
 *     navigation timing API. Defaults to 'responseEnd'.
 * @return {number} The time duration
 */
function measureDuration(mark, opt_reference) {
  var reference = opt_reference || 'responseEnd';
  var name = reference + ':' + mark;

  // Clears any existing measurements with the same name.
  performance.clearMeasures(name);

  // Creates a new measurement from the reference point to the specified mark.
  // If more than one mark with this name exists, the most recent one is used.
  performance.measure(name, reference, mark);

  // Gets the value of the measurement just created.
  var measure = performance.getEntriesByName(name)[0];

  // Returns the measure duration.
  return measure.duration;
}

أخيرًا، عليك تحميل نص perf-analytics.js البرمجي من index.html. لإجراء ذلك، أضِف علامة النص البرمجي التالية إلى مستندك الرئيسي. احرص على إضافته في النهاية حتى لا يتعارض مع تحميل الموارد الأخرى.

<!-- Start performance analytics -->
<script async src="perf-analytics.js"></script>
<!-- End performance analytics -->

بعد إكمال هذه الخطوة، يجب أن يتطابق الرمز مع الرمز في دليل 01-css ضمن مستودع الدرس التطبيقي.

إذا حمّلت الصفحة في متصفّح وفتحت وحدة تحكّم المطوّرين، من المفترض أن يظهر لك ناتج مشابه لما يلي:

Screen Shot 2016-05-17 at 11.13.02 AM.png

يتم عادةً تحميل خطوط الويب من خلال ورقة أنماط خارجية، كما هو موضّح في ملف العرض التوضيحي الأولي:

<!-- Start fonts -->
<link href="https://fonts.googleapis.com/css?family=Roboto:400,700,400italic" rel="stylesheet">
<!-- End fonts -->

بما أنّ هذا هو علامة <link> لملف CSS، قد يبدو تحديد وقت تحميل الخطوط وجاهزيتها للاستخدام بسيطًا مثل إضافة علامة داخل علامة <script> مباشرةً بعد <link>، تمامًا كما في الخطوة 1.

لسوء الحظ، الأمر ليس بهذه البساطة.

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

المشكلة هي أنّ المتصفّح يمكنه إنشاء CSSOM بدون تنزيل الخط فعليًا، ما يعني أنّه في حال إضافة علامة من خلال علامة نص برمجي مضمّنة إلى نموذج المستند (DOM) مباشرةً بعد علامة <link> لورقة أنماط الخط، من المحتمل أن تظهر العلامة قبل تحميل الخط بالكامل.

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

يمكن تحميل معظم خطوط الويب (بما في ذلك خطوط Google وTypekit وfont.com) من خلال النص البرمجي webfont.js الذي شاركت Google وTypekit في تطويره.

لتعديل المستند الرئيسي لاستخدام webfont.js لتحميل الخطوط (بدلاً من العلامة <link>)، استبدِل قسم الخطوط في الرمز بما يلي:

<!-- Start fonts -->
<script>
window.WebFontConfig = {
  google: {families: ['Roboto:400,700,400italic']},
  timeout: 10000,
  active: function() {
    performance.mark('fonts:active');
  }
};
</script>
<script async src="https://ajax.googleapis.com/ajax/libs/webfont/1/webfont.js"></script>
<!-- End fonts -->

هناك ملاحظتان مهمّتان يجب أخذهما في الاعتبار بشأن الرمز أعلاه:

  • تنشئ علامة "fonts:active" في دالة معاودة الاتصال النشطة، ما يتيح لك قياس المدة التي استغرقتها عملية تحميل الخطوط لاحقًا.
  • تحتوي العلامة <script> التي تحمّل webfonts.js على السمة async، لذا لن تحظر تحليل بقية المستند أو عرضه (وهذا لا ينطبق على العلامات <link>).

على الرغم من أنّ الرموز أعلاه تنشئ علامة "fonts:active"، إلا أنّ قياس هذه العلامة وتسجيلها في وحدة التحكّم ليس بسيطًا كما كان الحال مع العلامة "css:unblock". والسبب هو أنّ تحميل الخطوط يتم الآن بشكل غير متزامن، لذا إذا حاولت قياس العلامة "fonts:active" في معالج window.onload (كما فعلت مع "css:unblock")، من المحتمل جدًا ألا يتم تحميل الخط بعد.

لحلّ هذه المشكلة، يمكنك إنشاء وعد يتم تنفيذه بعد تحميل الخط. تنفِّذ الدالة التالية هذا الإجراء نيابةً عنك. انسخ الرمز والصقه في ملف perf-analytics.js:

/**
 * Creates a promise that is resolved once the web fonts are fully load or
 * is reject if the fonts fail to load. The resolved callback calculates the
 * time duration between the responseEnd timing event and when the web fonts
 * are downloaded and active. If an error occurs loading the font, this fact
 * is logged to the console.
 */
function measureWebfontPerfAndFailures() {
  new Promise(function(resolve, reject) {
    // The classes `wf-active` or `wf-inactive` are added to the <html>
    // element once the fonts are loaded (or error).
    var loaded = /wf-(in)?active/.exec(document.documentElement.className);
    var success = loaded && !loaded[1]; // No "in" in the capture group.
    // If the fonts are already done loading, resolve immediately.
    // Otherwise resolve/reject in the active/inactive callbacks, respectively.
    if (loaded) {
      success ? resolve() : reject();
    }
    else {
      var originalAciveCallback = WebFontConfig.active;
      WebFontConfig.inactive = reject;
      WebFontConfig.active = function() {
        originalAciveCallback();
        resolve();
      };
      // In case the webfont.js script fails to load, always reject the
      // promise after the timeout amount.
      setTimeout(reject, WebFontConfig.timeout);
    }
  })
  .then(function() {
    console.log('Fonts', 'active', measureDuration('fonts:active'));
  })
  .catch(function() {
    console.error('Error loading web fonts')
  });
}

عدِّل أيضًا معالج window.onload لاستدعاء هذه الدالة الجديدة.

window.onload = function() {
  measureCssUnblockTime();
  measureWebfontPerfAndFailures();
};

بعد إكمال هذه الخطوة، يجب أن يتطابق الرمز مع الرمز في دليل 02-fonts ضمن مستودع الدرس التطبيقي.

إذا حمّلت الصفحة في متصفّح وفتحت وحدة تحكّم المطوّرين، من المفترض أن يظهر لك ناتج مشابه لما يلي:

Screen Shot 2016-05-17 at 11.13.22 AM.png

إنّ معرفة الوقت الذي تكون فيه الصورة مرئية ليس بسيطًا كما قد يبدو. من الخطوات السابقة، تعرف أنّ أوراق الأنماط وعلامات <script> المتزامنة يمكن أن تحظر العرض والتفسير وتنفيذ النصوص البرمجية. قد لا تعرف أنّ أياً منهما لا يحظر أداة فحص التحميل المُسبَق في المتصفّح.

تتضمّن جميع المتصفّحات الحديثة فاحص التحميل المُسبَق كإحدى المحاولات العديدة لتحسين الأداء، حتى في المواقع الإلكترونية التي لا تركّز على الأداء والتي تحتوي على الكثير من مواد العرض التي تحظر عرض المحتوى. الفكرة هي أنّه على الرغم من أنّ بعض مواد العرض قد تحظر تحليل أو عرض أو تنفيذ النصوص البرمجية، ليس من الضروري أن تحظر عمليات التنزيل. لذا، يفحص المتصفّح ملف HTML قبل أن يبدأ في إنشاء نموذج المستند (DOM)، ويبحث عن مواد العرض التي يمكنه بدء تنزيلها على الفور.

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

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

لذلك، لمعرفة متى تكون الصورة مرئية، عليك التعامل مع كلتا الحالتين.

يمكنك إجراء ذلك عن طريق إضافة علامات في معالج onload لكل صورة، بالإضافة إلى علامة <script> مضمّنة بعد آخر صورة في DOM مباشرةً. الفكرة هي أنّ العلامة التي تظهر آخرًا ستكون العلامة التي تمثّل وقت ظهور جميع الصور.

لإضافة علامات لكلّ من وقت تحميل الصور ووقت عرضها، عدِّل رمز الصور في index.html على النحو التالي:

<!-- Start images -->
<div class="gallery">
  <img onload="performance.mark('img:visible')" src="http://lorempixel.com/380/200/animals/1/">
  <img onload="performance.mark('img:visible')" src="http://lorempixel.com/380/200/animals/2/">
  <img onload="performance.mark('img:visible')" src="http://lorempixel.com/380/200/animals/3/">
</div>
<script>performance.mark('img:visible')</script>
<!-- End images -->

بما أنّ طريقة performance.measure لاسم علامة معيّن ستستخدِم دائمًا العلامة الأخيرة (إذا عثرت على علامات متعدّدة بالاسم نفسه)، يمكن استخدام الدالة المساعدة measureDuration في ملف perf-analytics.js لهذا الغرض بدون أي تعديلات إضافية:

/**
 * Calculates the time duration between the responseEnd timing event and when
 * all images are loaded and visible on the page, then logs that value to the
 * console.
 */
function measureImagesVisibleTime() {
  console.log('Images', 'visible', measureDuration('img:visible'));
}

أضِف الدالة أعلاه إلى ملف perf-analytics.js، ثم عدِّل معالج window.onload لاستدعائه:

window.onload = function() {
  measureCssBlockTime();
  measureWebfontPerfAndFailures();
  measureImagesVisibleTime();
};

بعد إكمال هذه الخطوة، يجب أن يتطابق الرمز مع الرمز في دليل 03-images ضمن مستودع الدرس التطبيقي.

إذا حمّلت الصفحة في متصفّح وفتحت وحدة تحكّم المطوّرين، من المفترض أن يظهر لك ناتج مشابه لما يلي:

Screen Shot 2016-05-17 at 11.13.39 AM.png

بما أنّ علامات <script> التي لا تتضمّن السمة async تحظر تحليل نموذج المستند (DOM) إلى أن يتم تنزيلها وتنفيذها، يمكنك تحديد النقطة التي انتهى فيها تنفيذ جميع النصوص البرمجية من خلال إضافة علامة في علامة نص برمجي مضمّن مباشرةً بعد آخر علامة <script> متزامنة في نموذج المستند (DOM).

يُرجى العِلم أنّ استخدام معالِجات onload لن ينجح في هذه الحالة لأنّ المتصفّح يجب أن ينفّذ النص البرمجي بعد تحميله، ويستغرق ذلك وقتًا. يمكن أن يكون النص البرمجي الذي يتم تحميله بسرعة ولكن تنفيذه بطيء سيئًا تمامًا مثل النص البرمجي الذي يتم تحميله ببطء.

لتتبُّع وقت تحميل جميع JavaScript وتنفيذها في المستند الرئيسي، عدِّل قسم JavaScript في index.html باستخدام الرمز التالي:

<!-- Start JavaScript -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
<script>performance.mark('js:execute');</script>
<!-- End JavaScript -->

سيؤدي ذلك إلى إضافة علامة باسم "js:execute" مباشرةً بعد تنزيل النصوص البرمجية لمكوّنات jQuery الإضافية ومكوّنات Bootstrap الإضافية وتنفيذها.

لقياس المدة التي يستغرقها ذلك، أضِف الدالة التالية إلى perf-analytics.js:

/**
 * Calculates the time duration between the responseEnd timing event and when
 * all synchronous JavaScript files have been downloaded and executed, then
 * logs that value to the console.
 */
function measureJavaSciptExecutionTime() {
  console.log('JavaScript', 'execute', measureDuration('js:execute'));
}

بعد ذلك، استدعِها من معالج window.onload:

window.onload = function() {
  measureCssBlockTime();
  measureWebfontPerfAndFailures();
  measureImagesVisibleTime();
  measureJavaSciptExecutionTime();
};

بعد إكمال هذه الخطوة، يجب أن يتطابق الرمز مع الرمز في دليل 04-javascript ضمن مستودع الدرس التطبيقي.

إذا حمّلت الصفحة في متصفّح وفتحت وحدة تحكّم المطوّرين، من المفترض أن يظهر لك ناتج مشابه لما يلي:

Screen Shot 2016-05-17 at 11.14.03 AM.png

لا تتوافق بعض المتصفحات مع JavaScript promises أو واجهات برمجة التطبيقات User Timing، وإذا شغّلت الرمز الذي كتبته حتى الآن في متصفح لا يتوافق مع إحدى هذه الميزات، ستظهر لك أخطاء.

ولحلّ هذه المشكلة، يمكنك استخدام ميزة "رصد الميزات". أضِف مقتطف الرمز التالي مباشرةً قبل قسم الخطوط. يرصد سطر JavaScript هذا إمكانية استخدام طريقة performance.mark، لذا يجب إضافته إلى الصفحة قبل استخدام هذه الطريقة:

<!-- Start feature detects -->
<script>window.__perf = window.performance && performance.mark;</script>
<!-- End feature detects -->

بعد ذلك، في أي مكان في index.html حيث تستدعي performance.mark، أضِف البادئة مع ميزة رصد التوافق. في ما يلي مثال يعدّل الرمز في كتلة الصورة، ولكن يجب التأكّد من تعديل الأقسام الأخرى أيضًا.

<!-- Start images -->
<div class="gallery">
  <img onload="__perf && performance.mark('img:visible')" src="http://lorempixel.com/380/200/animals/1/">
  <img onload="__perf && performance.mark('img:visible')" src="http://lorempixel.com/380/200/animals/2/">
  <img onload="__perf && performance.mark('img:visible')" src="http://lorempixel.com/380/200/animals/3/">
</div>
<script>__perf && performance.mark('img:visible')</script>
<!-- End images -->

بعد ضبط المتغيّر __perf على window، يمكنك أيضًا استخدامه في perf-analytics.js للتأكّد من عدم استدعاء طريقة غير متوافقة مع المتصفّح الحالي.

يجب تعديل الدالة measureDuration لإضافة الشرط التالي:

function measureDuration(mark, opt_reference) {
  if (window.__perf) {
    // ...
  }
}

أخيرًا، بما أنّ بعض المتصفّحات لا تتوافق مع JavaScript promises، يجب أيضًا تضمين الدالة measureWebfontPerfAndFailures في عبارة شرطية:

function measureWebfontPerfAndFailures() {
  if (window.Promise) {
    // ...
  }
}

من المفترض أن تتمكّن الآن من تشغيل الرمز في أي متصفّح بدون مشاكل.

بعد إكمال هذه الخطوة، يجب أن يتطابق الرمز مع الرمز في دليل 05-feature-detects ضمن مستودع الدرس التطبيقي.

الخطوة الأخيرة في هذا الدرس العملي هي أخذ البيانات التي يتم تسجيلها في وحدة التحكّم وإرسالها إلى "إحصاءات Google" بدلاً من ذلك. وقبل أن تتمكّن من إرسال البيانات إلى "إحصاءات Google"، عليك إضافة مكتبة analytics.js ومقتطف التتبُّع التلقائي إلى صفحتك.

أضِف الرمز التالي إلى index.html بعد مجموعة JavaScript الرئيسية ولكن قبل تحميل النص البرمجي perf-analytics.js:

<!-- Start analytics tracking snippet -->
<script>
window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;
ga('create', 'UA-XXXXX-Y', 'auto');
ga('send', 'pageview');
</script>
<script async src="https://www.google-analytics.com/analytics_debug.js"></script>
<!-- End analytics tracking snippet -->

إذا سبق لك إضافة "إحصاءات Google" إلى موقع إلكتروني، ستعرف أنّه عليك استبدال العنصر النائب "UA-XXXXX-Y" برقم تعريف التتبّع الذي تلقّيته عند إنشاء موقع جديد في "إحصاءات Google".

ينفّذ مقتطف التتبُّع analytics.js أربعة إجراءات رئيسية:

  • تنشئ هذه السمة عنصر <script> غير متزامن يعمل على تنزيل مكتبة JavaScript‏ analytics.js.
  • تُنشئ هذه الدالة دالة ga() عمومية (تُعرف باسم قائمة انتظار أوامر ga()‎) تتيح لك جدولة الأوامر ليتم تنفيذها بعد تحميل مكتبة analytics.js وتجهيزها.
  • تضيف هذه السمة أمرًا إلى قائمة انتظار الأوامر ga() من أجل إنشاء عنصر متتبّع جديد للموقع المحدّد من خلال المَعلمة "UA-XXXXX-Y".
  • يضيف هذا الأمر أمرًا آخر إلى قائمة انتظار الأوامر ga() من أجل إرسال مشاهدة صفحة إلى "إحصاءات Google" للصفحة الحالية.

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

تتيح "إحصاءات Google" عدة أنواع من بيانات التفاعل: مشاهدات الصفحة والأحداث والتفاعلات على وسائل التواصل الاجتماعي والاستثناءات و (أخيرًا وليس آخرًا) أوقات المستخدمين. لإرسال بيانات توقيت المستخدِم إلى "إحصاءات Google"، يمكنك استخدام توقيع الأمر التالي:

ga('send', 'timing', timingCategory, timingVar, timingValue);

حيث timingCategory هي سلسلة تتيح لك تنظيم النتائج المتعلقة بالتوقيت في مجموعة منطقية، وtimingVar هو المتغيّر الذي تقيسه، وtimingValue هي مدة الوقت الفعلية بالمللي ثانية.

لمعرفة كيفية عمل ذلك عمليًا، يمكن تعديل عبارة console.log في الدالة measureCssUnblockTime على النحو التالي:

ga('send', 'timing', 'CSS', 'unblock', measureDuration('css:unblock'));

على الرغم من أنّ الرمز البرمجي أعلاه سيعمل في بعض الحالات، هناك نقطتان مهمّتان يجب الانتباه إليهما:

  • عدّلت الخطوة السابقة الدالة measureDuration لتنفيذها فقط إذا كان المتصفّح يتوافق مع User Timings API، ما يعني أنّها ستعرض أحيانًا القيمة undefined. بما أنّه لا يوجد سبب لإرسال بيانات غير محدّدة إلى "إحصاءات Google" (في بعض الحالات، قد يؤدي ذلك إلى إفساد تقاريرك)، يجب ألا ترسل نتيجة التوقيت هذه إلا إذا عرضت measureDuration قيمة.
  • عندما تعرض الدالة measureDuration قيمة، تكون هذه القيمة DOMHighResTimeStamp، وستكون دقتها أكبر من جزء من الألف من الثانية. بما أنّ timingValue في "إحصاءات Google" يجب أن يكون عددًا صحيحًا، عليك تقريب القيمة التي تعرضها measureDuration.

لتجنُّب هذه المشاكل، عدِّل عبارة الإرجاع في الدالة measureDuration لتقريب القيمة المعروضة:

function measureDuration(mark, opt_reference) {
  if (window.__perf) {
    // ...
    return Math.round(measure.duration);
  }
}

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

function measureCssUnblockTime() {
  var cssUnblockTime = measureDuration('css:unblock');
  if (cssUnblockTime) {
    ga('send', 'timing', 'CSS', 'unblock', cssUnblockTime);
  }
}

عليك إجراء تعديلات مماثلة على جميع دوال القياس الأخرى. بعد اكتمال العملية، يجب أن يبدو ملف perf-analytics.js النهائي على النحو التالي:

window.onload = function() {
  measureCssUnblockTime();
  measureWebfontPerfAndFailures();
  measureImagesVisibleTime();
  measureJavaSciptExecutionTime();
};


/**
 * Calculates the time duration between the responseEnd timing event and when
 * the CSS stops blocking rendering, then sends this measurement to Google
 * Analytics via a timing hit.
 */
function measureCssUnblockTime() {
  var cssUnblockTime = measureDuration('css:unblock');
  if (cssUnblockTime) {
    ga('send', 'timing', 'CSS', 'unblock', cssUnblockTime);
  }
}


/**
 * Calculates the time duration between the responseEnd timing event and when
 * the web fonts are downloaded and active, then sends this measurement to
 * Google Analytics via a timing hit. If an error occurs loading the font, an
 * error event is sent to Google Analytics.
 */
function measureWebfontPerfAndFailures() {
  if (window.Promise) {
    new Promise(function(resolve, reject) {
      var loaded = /wf-(in)?active/.exec(document.documentElement.className);
      var success = loaded && !loaded[1]; // No "in" in the capture group.
      if (loaded) {
        success ? resolve() : reject();
      }
      else {
        var originalAciveCallback = WebFontConfig.active;
        WebFontConfig.inactive = reject;
        WebFontConfig.active = function() {
          originalAciveCallback();
          resolve();
        };
        // In case the webfont.js script failed to load.
        setTimeout(reject, WebFontConfig.timeout);
      }
    })
    .then(function() {
      var fontsActiveTime = measureDuration('fonts:active');
      if (fontsActiveTime) {
        ga('send', 'timing', 'Fonts', 'active', fontsActiveTime);
      }
    })
    .catch(function() {
      ga('send', 'event', 'Fonts', 'error');
    });
  }
}


/**
 * Calculates the time duration between the responseEnd timing event and when
 * all images are loaded and visible on the page, then sends this measurement
 * to Google Analytics via a timing hit.
 */
function measureImagesVisibleTime() {
  var imgVisibleTime = measureDuration('img:visible');
  if (imgVisibleTime) {
    ga('send', 'timing', 'Images', 'visible', imgVisibleTime);
  }
}


/**
 * Calculates the time duration between the responseEnd timing event and when
 * all synchronous JavaScript files are downloaded and executed, then sends
 * this measurement to Google Analytics via a timing hit.
 */
function measureJavaSciptExecutionTime() {
  var jsExecuteTime = measureDuration('js:execute');
  if (jsExecuteTime) {
    ga('send', 'timing', 'JavaScript', 'execute', jsExecuteTime);
  }
}


/**
 * Accepts a mark name and an optional reference point in the navigation timing
 * API and returns the time duration between the reference point and the last
 * mark (chronologically). The return value is rounded to the nearest whole
 * number to be compatible with Google Analytics.
 * @param {string} mark The mark name.
 * @param {string=} opt_reference An optional reference point from the
 *     navigation timing API. Defaults to 'responseEnd'.
 * @return {?number} The time duration as an integer or undefined if no
 *     matching marks can be found.
 */
function measureDuration(mark, opt_reference) {
  if (window.__perf) {
    var reference = opt_reference || 'responseEnd';
    var name = reference + ':' + mark;

    // Clears any existing measurements with the same name.
    performance.clearMeasures(name);

    // Creates a new measurement from the reference point to the specified mark.
    // If more than one mark with this name exists, the most recent one is used.
    performance.measure(name, reference, mark);

    // Gets the value of the measurement just created.
    var measure = performance.getEntriesByName(name)[0];

    // Returns the measure duration.
    return Math.round(measure.duration);
  }
}

ويجب أن يظهر ملف index.html النهائي على النحو التالي:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Performance Analytics Demo</title>

  <!-- Start navigation timing feature detect -->
  <script>window.__perf = window.performance && performance.mark;</script>
  <!-- End navigation timing feature detect -->

  <!-- Start fonts -->
  <script>
  window.WebFontConfig = {
    google: {families: ['Roboto:400,700,400italic']},
    timeout: 10000,
    active: function() {
      __perf && performance.mark('fonts:active');
    }
  };
  </script>
  <script async src="https://ajax.googleapis.com/ajax/libs/webfont/1/webfont.js"></script>
  <!-- End fonts -->

  <!-- Start CSS -->
  <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet">
  <style>
    body { font-family: Roboto, sans-serif; margin: 1em; }
    img { float: left; height: auto; width: 33.33%; }
    .gallery { overflow: hidden; }
  </style>
  <script>__perf && performance.mark('css:unblock');</script>
  <!-- End CSS -->

</head>
<body>

  <div class="container">

    <!-- Start images -->
    <div class="gallery">
      <img onload="__perf && performance.mark('img:visible')" src="http://lorempixel.com/380/200/animals/1/">
      <img onload="__perf && performance.mark('img:visible')" src="http://lorempixel.com/380/200/animals/2/">
      <img onload="__perf && performance.mark('img:visible')" src="http://lorempixel.com/380/200/animals/3/">
    </div>
    <script>__perf && performance.mark('img:visible')</script>
    <!-- End images -->

    <h1>Performance Analytics Demo</h1>
    <p>Real performance data from real users.</p>

  </div>

  <!-- Start JavaScript -->
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
  <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
  <script>__perf && performance.mark('js:execute');</script>
  <!-- End JavaScript -->

  <!-- Start analytics tracking snippet -->
  <script>
  window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;
  ga('create', 'UA-XXXXX-Y', 'auto');
  ga('send', 'pageview');
  </script>
  <script async src="https://www.google-analytics.com/analytics.js"></script>
  <!-- End analytics tracking snippet -->

  <!-- Start performance analytics -->
  <script async src="perf-analytics.js"></script>
  <!-- End performance analytics -->

</body>
</html>

إذا حمّلت هذه الصفحة وألقيت نظرة على الطلبات في لوحة الشبكة، سيظهر لك ما يلي:

Screen Shot 2016-05-10 at 6.57.23 PM.png

هذا مفيد، ولكن قد يكون من الصعب الاطّلاع على هذه البيانات كطلب بترميز URL. وإذا لم تظهر لك هذه الطلبات لأي سبب كان، سيكون من الصعب جدًا تتبُّع مكان حدوث الخطأ.

عند التطوير محليًا، من الأفضل استخدام إصدار تصحيح الأخطاء من analytics.js، الذي سيسجّل معلومات مفيدة لتصحيح الأخطاء في وحدة التحكّم عند تنفيذ كل أمر من أوامر analytics.js. إذا لم

عدِّل عنوان URL الخاص بملف analytics.js في index.html إلى analytics_debug.js وافتح وحدة تحكّم المتصفّح، وستظهر لك عبارات مطبوعة بالشكل التالي:

Screen Shot 2016-05-10 at 6.54.13 PM.png

بعد أن فهمت كيفية تنفيذ قياس الأداء لصفحة العرض التوضيحي هذه، يمكنك محاولة إضافتها إلى موقعك الإلكتروني وإرسال بيانات المستخدمين الفعليين إلى "إحصاءات Google".

إعداد التقارير عن البيانات التي جمعتها

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

للوصول إلى تقارير "توقيت المستخدم" في "إحصاءات Google"، انقر على علامة التبويب "إعداد التقارير" في أعلى الصفحة، ثمّ اختَر "السلوك > سرعة الموقع > توقيتات المستخدم" من شريط التنقّل الجانبي (أو اتّبِع التعليمات لعرض تقرير توقيتات المستخدم من "مركز المساعدة").

عند تحميل تقرير "مدّة استخدام المستخدم" في "إحصاءات Google"، من المفترض أن تتمكّن من الاطّلاع على فئات المدّة الزمنية التي تتوافق مع البيانات التي أرسلتها. انقر على أيّ من هذه الخيارات للاطّلاع على رسومات بيانية تفصيلية لبيانات التوقيت. الصورة التالية هي مثال على أوقات تحميل الخطوط على موقع إلكتروني حقيقي يستخدم Google Fonts خلال آخر 24 ساعة.

Screen Shot 2016-05-10 at 7.16.07 PM.png

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

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

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

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

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

على سبيل المثال، اطّلِع على قائمة الأسئلة التالية، وكيفية استخدام المعرفة التي اكتسبتها في هذا الدرس العملي البرمجي لاستخدام "إحصاءات Google" للإجابة عنها:

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

أخيرًا، إذا أردت الاطّلاع على مزيد من المعلومات حول أداء الويب أو "إحصاءات Google"، إليك بعض المراجع الرائعة لمساعدتك على البدء: