פריסה היא המקום שבו הדפדפן מזהה את המידע הגאומטרי עבור אלמנטים – הגודל והמיקום שלהם בדף. לכל רכיב יהיה מידע מפורש או מרומז על שינוי הגודל על סמך ה-CSS שבו נעשה שימוש, תוכן הרכיב או רכיב הורה. התהליך נקרא 'פריסה' ב-Chrome.
פריסה היא המקום שבו הדפדפן מזהה את המידע הגאומטרי עבור אלמנטים: הגודל והמיקום שלהם בדף. לכל רכיב יהיה מידע מפורש או מרומז על שינוי הגודל על סמך ה-CSS שבו נעשה שימוש, תוכן הרכיב או רכיב הורה. התהליך נקרא 'פריסה' ב-Chrome (ובדפדפנים נגזרים כמו Edge) ו-Safari. ב-Firefox התהליך נקרא Reflow, אבל התהליך זהה.
בדומה לחישובי סגנונות, החששות המיידיים של עלות הפריסה הם:
- מספר הרכיבים שנדרשת עבורם פריסה, שהיא תוצר לוואי של גודל ה-DOM של הדף.
- המורכבות של הפריסות האלה.
סיכום
- לפריסה יש השפעה ישירה על זמן האחזור של אינטראקציה
- בדרך כלל הפריסה בהיקף של כל המסמך.
- מספר רכיבי ה-DOM ישפיע על הביצועים; כדאי להימנע ככל האפשר מהפעלת הפריסה.
- עדיף להימנע מפריסות סינכרוניות ומכוח היסט של פריסה; לקרוא את ערכי הסגנון ולבצע שינויי סגנון.
השפעות הפריסה על זמן האחזור של אינטראקציה
כשמשתמש מקיים אינטראקציה עם הדף, האינטראקציות האלה צריכות להיות מהירות ככל האפשר. משך הזמן שחולף עד להשלמת האינטראקציה – מסתיים כשהדפדפן מציג את הפריים הבא כדי להציג את התוצאות של האינטראקציה – נקרא זמן אחזור של אינטראקציה. זהו היבט של ביצועי הדף שמודד המדד אינטראקציה עד התגובה הבאה.
משך הזמן שנדרש לדפדפן להציג את הפריים הבא בתגובה לאינטראקציה של משתמש נקרא עיכוב ההצגה של האינטראקציה. מטרת האינטראקציה היא לספק משוב חזותי כדי לאותת למשתמש שמשהו קרה. עדכונים חזותיים עשויים לכלול כמות מסוימת של עבודת פריסה כדי להשיג את המטרה הזו.
כדי שה-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 גדולים יותר בדרך כלל יגרמו לעלויות פריסה גבוהות יותר.
הימנעות מפריסות סינכרוניות מאולצות
במשלוח מסגרת למסך, יש את הסדר הבא:
קודם כל מפעילים את 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`;
}
}
הקוד הזה עובר בלולאה (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, שמקבץ אוטומטית את הקריאות והכתיבה שלכם, ולמנוע מצב שבו לא תפעילו בטעות פריסות סינכרוניות מאולצות או ויסות נתונים (throwing) בטעות.
תמונה ראשית (Hero) מ-Unbounce, מאת Hal Gatewood.