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

תאריך פרסום: 20 במרץ 2015, תאריך עדכון אחרון: 7 במאי 2025

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

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

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

סיכום

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

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

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

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

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

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

כשמשנים סגנונות, הדפדפן בודק אם יש שינויים שדורשים חישוב של הפריסה ועדכון של עץ הרינדור. שינויים ב'מאפיינים גיאומטריים', כמו width, ‏ height, ‏ left או top, מחייבים פריסה.

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

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

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

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

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

DevTools שמוצגים בו כמה בלוקים של פריסה בצבע סגול.
כלי הפיתוח ל-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`;
  }
}

הקוד הזה מבצע לולאה על קבוצה של פסקאות ומגדיר את רוחב כל פסקאה כך שיתאים לרוחב של רכיב שנקרא 'box'. זה נראה לא מזיק, אבל הבעיה היא שבכל חזרה של הלולאה נקרא ערך סגנון (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`;
  }
}

זיהוי של פריסות סינכרוניות מאולצות ושל טרישה

ב-DevTools יש תובנה לגבי Forced Reflow, שבעזרתה אפשר לזהות במהירות מקרים של פריסות סינכרוניות מאולצות (שנקראות גם 'Forced Reflow'):

כלי הפיתוח שמוצגת בהם תובנה לגבי זרימה כפויה, ומזוהה בהן פונקציה בשם &#39;w&#39; שגורמת לזרימה כפויה.
תובנה לגבי אילוץ זרימה מחדש ב-Chrome DevTools

אפשר לזהות פריסות סינכרוניות מאולצות גם בשדה באמצעות שיוך סקריפט של Long Animation Frame API באמצעות המאפיין forcedStyleAndLayoutDuration.