מדדים מותאמים אישית

יש חשיבות רבה ליצירת מדדים ממוקדי-משתמש שתוכלו למדוד באופן אוניברסלי בכל אתר נתון. המדדים האלה מאפשרים:

  • להבין את האופן שבו משתמשים אמיתיים חווים את האינטרנט באופן כללי.
  • להשוות את האתר שלך לאתר של מתחרה.
  • בכלי לניתוח נתונים אפשר לעקוב אחרי נתונים שימושיים ופרקטיים בלי לכתוב קוד מותאם אישית.

מדדים אוניברסליים הם ערך בסיס טוב, אבל במקרים רבים צריך למדוד יותר מאשר רק המדדים האלה כדי ליהנות מהחוויה המלאה של האתר הספציפי.

מדדים מותאמים אישית מאפשרים לכם למדוד היבטים שקשורים לחוויית השימוש באתר, שעשויים להיות רלוונטיים רק לאתר שלכם, למשל:

  • כמה זמן לוקח לאפליקציה דף יחיד (SPA) לעבור מ'דף' אחד לאחר.
  • תוך כמה זמן מוצגים בדף נתונים שנשלפו ממסד נתונים עבור משתמשים שמחוברים לחשבון.
  • כמה זמן לוקח לאפליקציה שמעובדת בצד השרת (SSR) להגיב במשך הזמן.
  • שיעור ההיטים במטמון של משאבים שנטענו על ידי מבקרים חוזרים.
  • זמן האחזור של האירוע של אירועי קליק או מקלדת במשחק.

ממשקי API למדידת מדדים מותאמים אישית

בעבר, למפתחי אתרים לא היו הרבה ממשקי API ברמה נמוכה למדידת ביצועים, וכתוצאה מכך הם נאלצו לנקוט פריצות כדי למדוד אם האתר מניב ביצועים טובים.

לדוגמה, אפשר לקבוע אם ה-thread הראשי חסום עקב משימות JavaScript ממושכות, על ידי הרצת לולאת requestAnimationFrame וחישוב הדלתא בין כל פריים. אם הדלתא ארוכה משמעותית מקצב הפריימים במסך, אפשר לדווח על כך כמשימה ארוכה. עם זאת, לא מומלץ לבצע פריצות כאלה, כי הן משפיעות בפועל על הביצועים (למשל, על ידי התרוקנות הסוללה).

הכלל הראשון למדידת ביצועים יעילה הוא לוודא שהטכניקות שלכם למדידת הביצועים לא גורמות לבעיות בביצועים. לכן, לכל מדד מותאם אישית שאתם מודדים באתר, מומלץ להשתמש באחד מממשקי ה-API הבאים, אם אפשר.

ממשק API של Performance Observer

תמיכה בדפדפן

  • 52
  • 79
  • 57
  • 11

מקור

Performance Observer API הוא המנגנון שאוסף ומציג נתונים מכל ממשקי ה-API האחרים שקשורים לביצועים, שמפורטים בדף הזה. כדי לקבל נתונים טובים, חשוב להבין את המידע הזה.

אפשר להשתמש ב-PerformanceObserver כדי להירשם באופן פסיבי לאירועים שקשורים לביצועים. כך קריאות חוזרות ל-API מופעלות במהלך תקופות לא פעילות, כלומר הן בדרך כלל לא יפריעו לביצועי הדפים.

כדי ליצור PerformanceObserver, יש להעביר אליו קריאה חוזרת (callback) שתופעל בכל פעם שיישלחו רשומות חדשות של ביצועים. לאחר מכן אומרים לצופה לאילו סוגי רשומות להקשיב באמצעות השיטה observe():

// Catch errors since some browsers throw when using the new `type` option.
// https://bugs.webkit.org/show_bug.cgi?id=209216
try {
  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'});
} catch (e) {
  // Do nothing if the browser doesn't support this API.
}

בקטעים הבאים מפורטים כל סוגי הרשומות השונים שזמינים לתצפית, אבל בדפדפנים חדשים יותר, תוכלו לבדוק אילו סוגי רשומות זמינים דרך המאפיין PerformanceObserver.supportedEntryTypes הסטטי.

תצפיות על רשומות שכבר בוצעו

כברירת מחדל, אובייקטים מסוג PerformanceObserver יכולים לתעד רשומות רק בזמן שהן מתרחשות. הדבר עלול לגרום לבעיות אם רוצים לבצע טעינה מדורגת של הקוד של ניתוח הביצועים, כדי שהוא לא יחסום משאבים בעדיפות גבוהה יותר.

כדי לקבל ערכים היסטוריים (אחרי שנוצרו), צריך להגדיר את הדגל buffered לערך true כשקוראים לפונקציה observe(). הדפדפן יכלול ערכים היסטוריים ממאגר הנתונים הזמני של הביצועים בפעם הראשונה תתבצע קריאה חוזרת של PerformanceObserver.

po.observe({
  type: 'some-entry-type',
  buffered: true,
});

ממשקי API לביצועים מדור קודם שכדאי להימנע מהם

לפני ה-Performance Observer API, מפתחים יכלו לגשת לרשומות של ביצועים באמצעות שלוש השיטות הבאות שהוגדרו באובייקט performance:

אומנם יש תמיכה בממשקי ה-API האלה, אבל לא מומלץ להשתמש בהם כי הם לא מאפשרים לזהות מתי מתפרסמות רשומות חדשות. בנוסף, הרבה ממשקי API חדשים (כמו משימות ארוכות) לא נחשפים דרך האובייקט performance, הם נחשפים רק דרך PerformanceObserver.

אלא אם דרושה לך תאימות ספציפית עם Internet Explorer, עדיף להימנע מהשיטות האלה בקוד ולהשתמש ב-PerformanceObserver מעכשיו והלאה.

ממשק API של תזמון משתמש

User Timing API הוא 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');

ממשקי API כמו Date.now() או performance.now() מעניקים לכם יכולות דומות, אבל היתרון של השימוש ב-User Timing API הוא שהוא משתלב היטב עם כלים לשיפור הביצועים. לדוגמה, כלי הפיתוח ל-Chrome מציגים באופן חזותי מדידות של תזמון המשתמש בחלונית הביצועים וספקי שירותים רבים של ניתוח נתונים עוקבים באופן אוטומטי אחרי כל מדידות שמבצעים ושולחים את נתוני משך הזמן לקצה העורפי שלהם.

כדי לדווח על מדידות של תזמון משתמש, אפשר להשתמש ב-PerformanceObserver ולהירשם כדי לתעד רשומות מסוג measure:

// Catch errors since some browsers throw when using the new `type` option.
// https://bugs.webkit.org/show_bug.cgi?id=209216
try {
  // 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});
} catch (e) {
  // Do nothing if the browser doesn't support this API.
}

ממשק API של Long Tasks

תמיכה בדפדפן

  • 58
  • 79
  • x
  • x

מקור

בעזרת Long Tasks API אפשר לדעת מתי ה-thread הראשי של הדפדפן חסום למשך מספיק זמן כדי להשפיע על קצב הפריימים או על זמן האחזור של הקלט. ה-API ידווח על כל המשימות שבוצעו למשך יותר מ-50 אלפיות השנייה.

בכל פעם שצריך להריץ קוד יקר או לטעון ולהפעיל סקריפטים גדולים, כדאי לבדוק אם הקוד הזה חוסם את ה-thread הראשי. למעשה, הרבה מדדים ברמה גבוהה יותר מבוססים על ממשק ה-API של Long Tasks (כמו Time to Interactive (TTI) ו-Total Block Time (TBT)).

כדי לדעת מתי מתבצעות משימות ארוכות, אפשר להשתמש ב-PerformanceObserver ולהירשם כדי לתעד רשומות מסוג longtask:

// Catch errors since some browsers throw when using the new `type` option.
// https://bugs.webkit.org/show_bug.cgi?id=209216
try {
  // 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});
} catch (e) {
  // Do nothing if the browser doesn't support this API.
}

ממשק API לתזמון רכיבים

תמיכה בדפדפן

  • 77
  • 79
  • x
  • x

מקור

בעזרת המדד המהירות שבה נטען רכיב התוכן הכי גדול (LCP) אפשר לדעת מתי התמונה או גוש הטקסט הגדולים ביותר צוירו על המסך, אבל במקרים מסוימים רוצים למדוד את זמן העיבוד של רכיב אחר.

במקרים כאלה, צריך להשתמש ב-Element Timing API. ממשק ה-LCP API מבוסס למעשה על ה-Element Timing API ומוסיף דיווח אוטומטי על רכיב התוכן הגדול ביותר, אבל אפשר גם לדווח על רכיבים אחרים באופן מפורש על-ידי הוספה מפורשת של המאפיין elementtiming אליהם, ורישום Performance Observer כדי לתעד את סוג הרשומה element.

<img elementtiming="hero-image" />
<p elementtiming="important-paragraph">This is text I care about.</p>
<!-- ... -->

<script>
  // Catch errors since some browsers throw when using the new `type` option.
  // https://bugs.webkit.org/show_bug.cgi?id=209216
  try {
    // Create the performance observer.
    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});
  } catch (e) {
    // Do nothing if the browser doesn't support this API.
  }
</script>

API לתזמון אירוע

המדד מהירות התגובה לאינטראקציה באתר (INP) בודק את רמת הרספונסיביות הכוללת של הדף לפי כל האינטראקציות מסוג קליקים, הקשה ומקלדת לאורך חיי הדף. לרוב, ה-INP של דף הוא האינטראקציה שנמשכה הכי הרבה זמן, מרגע שהמשתמש הפעיל את האינטראקציה ועד לרגע שבו הדפדפן מצייר את הפריים הבא שמראה את התוצאה החזותית של הקלט של המשתמש.

המדד INP זמין ב-Event Timing API. ה-API הזה חושף כמה חותמות זמן שמתרחשות במהלך מחזור החיים של האירוע, כולל:

  • startTime: השעה שבה הדפדפן מקבל את האירוע.
  • processingStart: השעה שבה הדפדפן יכול להתחיל לעבד גורמים שמטפלים באירועים של האירוע.
  • processingEnd: השעה שבה הדפדפן מסיים להפעיל את כל הקוד הסינכרוני שהופעל על ידי גורמים מטפלים באירועים באירוע הזה.
  • duration: הזמן (מעוגל ל-8 אלפיות השנייה מטעמי אבטחה) שעובר מהרגע שבו הדפדפן מקבל את האירוע ועד שהוא מצליח לצבוע את הפריים הבא אחרי שמסיימים להריץ את כל הקוד הסינכרוני שהופעל על ידי גורמים מטפלים באירועים.

הדוגמה הבאה מראה איך להשתמש בערכים האלה כדי ליצור מדידות מותאמות אישית:

// Catch errors some browsers throw when using the new `type` option.
// https://bugs.webkit.org/show_bug.cgi?id=209216
try {
  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 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 time (ms): ${processingTime}`);
      console.log(`Total event duration (ms): ${duration}`);
      console.log(`Event type: ${eventType}`);
      console.log(target);
    });
  });

  // A durationThreshold of 16ms is necessary to surface more
  // interactions, since the default is 104ms. The minimum
  // durationThreshold is 16ms.
  po.observe({type: 'event', buffered: true, durationThreshold: 16});
} catch (error) {
  // Do nothing if the browser doesn't support this API.
}

API לתזמון משאבים

Resource Timing API מספק למפתחים תובנות מפורטות לגבי אופן הטעינה של משאבים בדף מסוים. למרות שם ה-API, המידע שהוא מספק אינו מוגבל רק לנתוני תזמון (למרות שיש שפע של נתונים כאלה). נתונים אחרים שניתן לגשת אליהם כוללים:

  • initiatorType: איך המשאב אוחזר: למשל דרך תג <script> או <link>, או מקריאה של fetch().
  • nextHopProtocol: הפרוטוקול שמשמש לאחזור המשאב, למשל h2 או quic.
  • encodedBodySize/decodedBodySize]: גודל המשאב בצורת המקודד או המפוענחת (בהתאמה)
  • transferSize: גודל המשאב שהועבר בפועל דרך הרשת. כשהמטמון ממלא משאבים, הערך הזה יכול להיות קטן בהרבה מה-encodedBodySize, ובמקרים מסוימים הוא יכול להיות אפס (אם לא נדרש אימות מחדש של המטמון).

אפשר להשתמש בתכונה transferSize של רשומות תזמון משאבים כדי למדוד מדד שיעור ההיטים במטמון או מדד של גודל כולל של משאב שנשמר במטמון. אפשר להשתמש בו כדי להבין איך האסטרטגיה של שמירת המשאבים במטמון משפיעה על הביצועים של מבקרים חוזרים.

בדוגמה הבאה מתועדים כל המשאבים שהדף ביקש, ומצביעה על כך שהמטמון מילא את כל המשאבים או לא.

// Catch errors since some browsers throw when using the new `type` option.
// https://bugs.webkit.org/show_bug.cgi?id=209216
try {
  // Create the performance observer.
  const po = new PerformanceObserver((list) => {
    for (const entry of list.getEntries()) {
      // If transferSize is 0, the resource was fulfilled via the cache.
      console.log(entry.name, entry.transferSize === 0);
    }
  });

  // Start listening for `resource` entries to be dispatched.
  po.observe({type: 'resource', buffered: true});
} catch (e) {
  // Do nothing if the browser doesn't support this API.
}

תמיכה בדפדפן

  • 57
  • 12
  • 58
  • 15

מקור

ה-Navigation Timing API דומה ל-Resource Timing API, אבל הוא מדווח רק על בקשות ניווט. גם סוג הרשומה navigation דומה לסוג הרשומה resource, אבל הוא מכיל מידע נוסף שספציפי רק לבקשות ניווט (כמו הפעלת האירועים DOMContentLoaded ו-load).

אחד מהמדדים שמפתחים רבים עוקבים אחריהם כדי להבין את זמן התגובה של השרת (זמן עד בייט ראשון (TTFB)) זמין באמצעות ה-API לתזמון הניווט – ובפרט את חותמת הזמן responseStart של הרשומה.

// Catch errors since some browsers throw when using the new `type` option.
// https://bugs.webkit.org/show_bug.cgi?id=209216
try {
  // Create the performance observer.
  const po = new PerformanceObserver((list) => {
    for (const entry of list.getEntries()) {
      // If transferSize is 0, the resource was fulfilled via the cache.
      console.log('Time to first byte', entry.responseStart);
    }
  });

  // Start listening for `navigation` entries to be dispatched.
  po.observe({type: 'navigation', buffered: true});
} catch (e) {
  // Do nothing if the browser doesn't support this API.
}

מפתחי מדד נוספים שמשתמשים ב-Service Worker עשויים להיות חשובים לגבי זמן ההפעלה של קובץ השירות לצורך בקשות ניווט. זו משך הזמן שעובר עד שהדפדפן מתחיל להפעיל את ה-thread של ה-Service Worker לפני שהוא יכול להתחיל ליירט אירועי אחזור.

זמן ההפעלה של קובץ שירות (service worker) לבקשת ניווט מסוימת ניתן לקבוע מהדלתא בין entry.responseStart ל-entry.workerStart.

// Catch errors since some browsers throw when using the new `type` option.
// https://bugs.webkit.org/show_bug.cgi?id=209216
try {
  // 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});
} catch (e) {
  // Do nothing if the browser doesn't support this API.
}

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 מממשקי ה-API 'תזמון משאבים' ו'תזמון ניווט'.

// Catch errors since some browsers throw when using the new `type` option.
// https://bugs.webkit.org/show_bug.cgi?id=209216
try {
  // 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});
} catch (e) {
  // Do nothing if the browser doesn't support this API.
}