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

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

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

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

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

סיכום

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

תמונה ראשית (Hero) מ-UnFlood, מאת האל גייטווד.