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

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

Jeremy Wagner
Jeremy Wagner
Paul Lewis

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

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

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

סיכום

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

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

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

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

כדי שה-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 פועל, כל ערכי הפריסה הישנים מהפריים הקודם ידועים וזמינים לשאילתות. לדוגמה, אם רוצים לכתוב את גובה הרכיב (נקרא לו 'תיבה') בתחילת המסגרת, אפשר לכתוב קוד כ��ה:

// 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, שמקבץ באופן אוטומטי את הקריאות והכתובות שלכם, ומונע הפעלה של פריסות סינכרוניות מאולצות או טרחה מיותרת בפריסת הרכיבים בטעות.