מומלץ להימנע מפריסות ופריסות גדולות ומורכבות

בפריט 'פריסה', הדפדפן קובע את המידע הגיאומטרי של הרכיבים – הגודל והמיקום שלהם בדף. לכל רכיב יהיה מידע מפורש או מרומז על שינוי הגודל על סמך ה-CSS שבו נעשה שימוש, תוכן הרכיב או רכיב הורה. התהליך הזה נקרא 'פריסה' ב-Chrome.

פריסה היא המקום שבו הדפדפן מזהה את המידע הגאומטרי עבור אלמנטים: הגודל והמיקום שלהם בדף. לכל רכיב יהיה מידע מפורש או מרומז על שינוי הגודל על סמך ה-CSS שבו נעשה שימוש, תוכן הרכיב או רכיב הורה. התהליך נקרא 'פריסה' ב-Chrome (ובדפדפנים נגזרים כמו Edge) וב-Safari. ב-Firefox התהליך נקרא Reflow, אבל הוא זהה למעשה.

בדומה לחישוב העלות של סגנונות, הגורמים המיידיים שמשפיעים על עלות הפריסה הם:

  1. מספר הרכיבים שנדרשת עבורם פריסה, שהיא תוצר לוואי של גודל ה-DOM של הדף.
  2. המורכבות של הפריסות האלה.

סיכום

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

ההשפעה של הפריסה על זמן האחזור של האינטראקציה

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

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

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

נמנעים ככל האפשר מפריסה

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

.box {
  width: 20px;
  height: 20px;
}

/**
  * Changing width and height
  * triggers layout.
  */

.box--expanded {
  width: 200px;
  height: 350px;
}

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

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

כלי הפיתוח מוצגים במשך זמן רב בפריסה.

כשבודקים לעומק את המעקב בדוגמאות שלמעלה, רואים שנדרשים יותר מ-28 אלפיות השנייה כדי להציג כל פריים בפריסת המסך. מכיוון שיש לנו 16 אלפיות השנייה כדי להציג פריים באנימציה במסך, הזמן הזה ארוך מדי. אפשר גם לראות ש-DevTools יציגו את גודל העץ (1,618 רכיבים במקרה הזה) ואת מספר הצמתים שנזקקו לפריסה (5 במקרה הזה).

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

הימנעות מפריסות סינכרוניות מאולצות

במשלוח מסגרת למסך, יש את הסדר הבא:

שימוש ב-flexbox כפריסה.

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

הדבר הראשון שחשוב לזכור הוא שכאשר JavaScript פועל, כל ערכי הפריסה הישנים מהפריים הקודם ידועים וזמינים לשאילתות. לדוגמה, אם רוצים לכתוב את הגובה של רכיב (נניח שנקרא לו 'box') בתחילת המסגרת, אפשר לכתוב קוד כזה:

// Schedule our function to run at the start of the frame:
requestAnimationFrame(logBoxHeight);

function logBoxHeight () {
  // Gets the height of the box in pixels and logs it out:
  console.log(box.offsetHeight);
}

הדברים עלולים להיות בעייתיים אם שיניתם את הסגנונות של התיבה לפני שמבקשים את הגובה של התיבה:

function logBoxHeight () {
  box.classList.add('super-big');

  // Gets the height of the box in pixels and logs it out:
  console.log(box.offsetHeight);
}

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

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

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

function logBoxHeight () {
  // Gets the height of the box in pixels and logs it out:
  console.log(box.offsetHeight);

  box.classList.add('super-big');
}

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

הימנעות מטרחה מיותרת בפריסה

יש דרך להחמיר את המצב של פריסות שתואמות לסנכרון בכפייה: לבצע הרבה מהן ברצף מהיר. כדאי להציץ בקוד הזה:

function resizeAllParagraphsToMatchBlockWidth () {
  // Puts the browser into a read-write-read-write cycle.
  for (let i = 0; i < paragraphs.length; i++) {
    paragraphs[i].style.width = `${box.offsetWidth}px`;
  }
}

הקוד הזה עובר בלולאה (loop) מעל קבוצה של פסקאות ומגדיר את הרוחב של כל פסקה כך שיתאים לרוחב הרכיב שנקרא 'תיבה'. זה נראה לא מזיק, אבל הבעיה היא שבכל חזרה של הלולאה נקרא ערך סגנון (box.offsetWidth) ולאחר מכן נעשה בו שימוש מיידי כדי לעדכן את הרוחב של הפסקה (paragraphs[i].style.width). בחזרה הבאה של הלולאה, הדפדפן צריך להביא בחשבון את העובדה שהסגנונות השתנו מאז הבקשה האחרונה של offsetWidth (בחזרה הקודמת), ולכן הוא צריך להחיל את שינויי הסגנון ולהריץ את הפריסה. זה יקרה בכל חזרה.

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

// Read.
const width = box.offsetWidth;

function resizeAllParagraphsToMatchBlockWidth () {
  for (let i = 0; i < paragraphs.length; i++) {
    // Now write.
    paragraphs[i].style.width = `${width}px`;
  }
}

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