עיבוד באינטרנט

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

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

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

הסברים על המונחים

קודם כל, נגדיר כמה מונחים שבהם נשתמש.

רינדור

רינדור בצד השרת (SSR)
עיבוד אפליקציה בשרת כדי לשלוח HTML ללקוח, במקום JavaScript.
רינדור בצד הלקוח (CSR)
רינדור אפליקציה בדפדפן באמצעות JavaScript כדי לשנות את ה-DOM.
עיבוד מראש
הפעלת אפליקציה בצד הלקוח בזמן הבנייה כדי לתעד את המצב הראשוני שלה כ-HTML סטטי.
מאזן נוזלים
הפעלת סקריפטים בצד הלקוח כדי להוסיף מצב אפליקציה ואינטראקטיביות ל-HTML שעבר רינדור בצד השרת. הידרציה מניחה ש-DOM לא משתנה.
התייבשות
למרות שהמונח הזה משמש לעיתים קרובות כדי לתאר את אותו הדבר כמו הידרציה, הוא מרמז על עדכון קבוע של ה-DOM עם המצב העדכני ביותר, כולל אחרי ההידרציה הראשונית.

ביצועים

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

רינדור בצד השרת

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

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

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

תרשים
    שמציג רינדור בצד השרת וביצוע JavaScript שמשפיעים על FCP ו-TTI.
FCP ו-TTI עם רינדור בצד השרת.

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

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

במסגרות, בספריות ובארכיטקטורות מודרניות רבות, אפשר לבצע רינדור של אותה אפליקציה גם בצד הלקוח וגם בצד השרת. אפשר להשתמש בטכניקות האלה כדי לבצע עיבוד בצד השרת. עם זאת, ארכיטקטורות שבהן הרינדור מתבצע גם בשרת וגם בצד הלקוח הן סוג נפרד של פתרון עם מאפייני ביצועים שונים מאוד ופשרות שונות. משתמשי React יכולים להשתמש בממשקי API של DOM בצד השרת או בפתרונות שמבוססים עליהם, כמו Next.js, כדי לבצע רינדור בצד השרת. משתמשי Vue יכולים להשתמש במדריך לרינדור בצד השרת של Vue או ב-Nuxt. ל-Angular יש Universal.

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

רינדור סטטי

רינדור סטטי מתבצע בזמן הבנייה. הגישה הזו מאפשרת לקבל ערך FCP מהיר, וגם ערך TBT ו-INP נמוכים יותר, בתנאי שמגבילים את כמות ה-JavaScript בצד הלקוח בדפים. בניגוד לרינדור בצד השרת, הוא גם מאפשר השגת TTFB מהיר באופן עקבי, כי אין צורך ליצור באופן דינמי את ה-HTML של הדף בשרת. באופן כללי, עיבוד סטטי פירושו יצירת קובץ HTML נפרד לכל כתובת URL מראש. בעזרת תגובות HTML שנוצרות מראש, אפשר לפרוס עיבודים סטטיים לכמה רשתות CDN כדי ליהנות מיתרונות של שמירת נתונים במטמון בנקודות קצה.

תרשים
    שמציג עיבוד סטטי והרצה אופציונלית של JavaScript שמשפיעים על FCP ו-TTI.
FCP ו-TTI עם עיבוד סטטי.

יש פתרונות לרינדור סטטי בכל הצורות והגדלים. כלים כמו Gatsby נועדו לגרום למפתחים להרגיש שהאפליקציה שלהם עוברת עיבוד דינמי, ולא נוצרת כשלב build. כלים ליצירת אתרים סטטיים כמו 11ty,‏ Jekyll ו-Metalsmith מתבססים על האופי הסטטי שלהם ומספקים גישה שמבוססת יותר על תבניות.

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

משתמשי React אולי מכירים את Gatsby, Next.js static export או את Navi. כל אלה מאפשרים ליצור דפים מרכיבים בקלות. עם זאת, רינדור סטטי ורינדור מראש מתנהגים באופן שונה: דפים שעברו רינדור סטטי הם אינטראקטיביים בלי שצריך להריץ הרבה JavaScript בצד הלקוח, בעוד שרינדור מראש משפר את ה-FCP של אפליקציית דף יחיד שצריך להפעיל אותה בצד הלקוח כדי שהדפים יהיו אינטראקטיביים באמת.

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

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

רינדור בצד השרת לעומת רינדור סטטי

רינדור בצד השרת הוא לא הפתרון הכי טוב לכל דבר, כי האופי הדינמי שלו עלול לגרום לעלויות משמעותיות של תקורה בחישוב. הרבה פתרונות של עיבוד בצד השרת לא מבצעים שטיפה מוקדמת, מעכבים את TTFB או מכפילים את הנתונים שנשלחים (לדוגמה, מצבים מוטבעים שמשמשים את JavaScript בצד הלקוח). ב-React, הפעולה renderToString() יכולה להיות איטית כי היא סינכרונית ומתבצעת בשרשור יחיד. ממשקי API חדשים יותר של React server DOM תומכים בהזרמה, שיכולה להעביר את החלק הראשוני של תגובת HTML לדפדפן מוקדם יותר, בזמן שהשאר עדיין נוצר בשרת.

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

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

עיבוד בצד השרת יכול גם להציג החלטות מעניינות כשבונים PWA. מה עדיף: להשתמש במטמון של service worker בדף מלא, או לבצע עיבוד בצד השרת של חלקי תוכן נפרדים?

רינדור בצד הלקוח

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

יכול להיות שיהיה קשה ליצור עיבוד בצד הלקוח ולשמור על מהירות העיבוד במכשירים ניידים. בעזרת עבודה קלה כדי לשמור על תקציב JavaScript מצומצם ולספק ערך בכמה שפחות הלוך ושוב, אפשר לגרום לעיבוד בצד הלקוח לשחזר כמעט את הביצועים של עיבוד טהור בצד השרת. כדי שהניתוח יפעל מהר יותר, כדאי להשתמש ב-<link rel=preload> כדי להעביר סקריפטים ונתונים קריטיים. מומלץ גם להשתמש בתבניות כמו PRPL כדי לוודא שהניווט הראשוני והניווטים הבאים ירגישו מיידיים.

תרשים
    שמציג איך עיבוד בצד הלקוח משפיע על FCP ו-TTI.
FCP ו-TTI עם עיבוד בצד הלקוח.

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

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

אם אתם מפתחים אפליקציות של דף יחיד, כדאי לכם לזהות את החלקים העיקריים בממשק המשתמש שמשותפים לרוב הדפים, כדי שתוכלו להשתמש בטכניקה של שמירת מטמון של מעטפת האפליקציה. בשילוב עם קובצי שירות (service workers), אפשר לשפר באופן משמעותי את הביצועים בביקורים חוזרים, כי הדף יכול לטעון את ה-HTML של מעטפת האפליקציה ואת התלות שלו מ-CacheStorage במהירות רבה.

הידרציה משלבת רינדור בצד השרת ורינדור בצד הלקוח

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

זהו פתרון יעיל, אבל הוא עלול לגרום לירידה משמעותית בביצועים.

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

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

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

מסמך HTML שמכיל ממשק משתמש שעבר סריאליזציה, נתונים מוטבעים וסקריפט bundle.js.

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

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

ההשפעות השליליות של רינדור בצד הלקוח על TTI.

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

רינדור בצד השרת וטעינה הדרגתית של נתונים

בשנים האחרונות חלו כמה התפתחויות בנושא עיבוד בצד השרת.

רינדור בצד השרת בסטרימינג מאפשר לשלוח HTML בחלקים שהדפדפן יכול לרנדר באופן הדרגתי בזמן שהוא מתקבל. כך התגי עיצוב מגיעים למשתמשים מהר יותר, וזמן הצגת התוכן הראשון (FCP) מתקצר. ב-React, הזרמים הם אסינכרוניים ב-renderToPipeableStream(), בהשוואה ל-renderToString() סינכרוני, מה שאומר שהטיפול בלחץ חוזר מתבצע בצורה טובה.

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

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

התייבשות חלקית

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

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

רינדור טריזומורפי

אם service workers הם אפשרות בשבילכם, כדאי לשקול שימוש ברינדור טריזומורפי. הטכניקה הזו מאפשרת להשתמש בעיבוד בצד השרת של סטרימינג לניווטים ראשוניים או לניווטים ללא JavaScript, ואז לאפשר ל-Service Worker לבצע עיבוד של HTML לניווטים אחרי שהוא מותקן. כך אפשר לשמור על עדכניות של רכיבים ותבניות שנשמרו במטמון, ולאפשר ניווט בסגנון SPA כדי לעבד תצוגות חדשות באותו סשן. הגישה הזו הכי יעילה כשניתן לשתף את אותו קוד של תבניות וניתוב בין השרת, דף הלקוח ו-service worker.

הדגמה של עיבוד טריסומורפי, שבה דפדפן ו-service worker מתקשרים עם השרת.

שיקולי SEO

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

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

ממשק המשתמש של בדיקת ההתאמה לניידים.

סיכום

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

אפשרויות העיבוד וההשפעה שלהן.

קרדיטים {:#credits}

תודה לכל מי שכתב ביקורות ונתן השראה:

ג'פרי פוזניק, חוסיין ג'ירדה, שובהי פאניקר, כריס הראלסון וסבסטיאן מרקבאגה.