אופטימיזציה של טעינת סקריפטים של צד שלישי ב-Next.js

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

Leena Sohoni
Leena Sohoni

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

סקריפטים של צד שלישי וההשפעה שלהם על הביצועים

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

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

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

ביקורות של Lighthouse בנושא 'הסרת משאבים שחוסמים את העיבוד' ו'צמצום השימוש בצדדים שלישיים'

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

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

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

ההתמקדות של Aurora בסקריפטים של צד שלישי

חלק משיתוף הפעולה של Aurora עם כלים ותבניות אינטרנט בקוד פתוח הוא לספק הגדרות ברירת מחדל חזקות וכלים מוגדרים מראש שיעזרו למפתחים לשפר היבטים של חוויית המשתמש, כמו ביצועים, נגישות, אבטחה והתאמה לנייד. בשנת 2021 התמקדנו בסיוע ל-framework stacks לשפר את חוויית המשתמש ואת המדדים של Core Web Vitals.

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

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

סידור סקריפטים של צד שלישי ללא רכיב מסגרת

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

  1. משתמשים במאפיין async או defer עם תגי <script> שמורים למעקב אחרי טעינה של סקריפטים של צד שלישי שאינם קריטיים, בלי לחסום את מנתח המסמך. סקריפטים שלא נדרשים לטעינת הדף הראשונית או לאינטראקציה הראשונה של המשתמש עשויים להיחשב כלא קריטיים.

       <script src="https://example.com/script1.js" defer></script>
       <script src="https://example.com/script2.js" async></script>
    
  2. יצירת חיבורים מראש למקורות נדרשים באמצעות קישור מראש ושליפה מוקדמת של נתוני DNS. כך ניתן להתחיל את ההורדה של סקריפטים קריטיים מוקדם יותר.

       <head>
           <link rel="preconnect" href="http://PreconnThis.com">
           <link rel="dns-prefetch" href="http://PrefetchThis.com">
       </head>
    
  3. טעינה איטית של משאבים מוטמעים ושל משאבים של צד שלישי אחרי שהתוכן הראשי של הדף מסתיים בטעינה, או כשהמשתמש גולל למטה לחלק של הדף שבו הם כלולים.

הרכיב Script ב-Next.js

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

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

// Example for beforeInteractive:
<Script src="https://cdnjs.cloudflare.com/polyfill/v3/polyfill.min.js?features=IntersectionObserverEntry%2CIntersectionObserver" strategy="beforeInteractive" />

// Example for afterInteractive (default):
<Script src="https://example.com/samplescript.js" />

// Example for lazyonload:
<Script src="https://connect.facebook.net/en_US/sdk.js" strategy="lazyOnload" />

למאפיין strategy יש שלושה ערכים אפשריים.

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

  2. afterInteractive: זוהי אסטרטגיית ברירת המחדל שחלה, והיא זהה לטעינה של סקריפט עם המאפיין defer. צריך להשתמש בו עבור סקריפטים שהדפדפן יכול להריץ אחרי שהדף הופך לאינטראקטיבי – לדוגמה, סקריפטים של ניתוח נתונים. המערכת של Next.js מזריצה את הסקריפטים האלה בצד הלקוח, והם פועלים אחרי שהדף מתמלא בנתונים. לכן, אלא אם צוין אחרת, כל הסקריפטים של צד שלישי שמוגדרים באמצעות רכיב ה-Script נדחים על ידי Next.js, וכך מתקבלת ברירת מחדל חזקה.

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

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

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

מדידת ההשפעה

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

סקריפטים של צד שלישי באפליקציית מסחר של Next.js

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

לפני אחרי
Google Tag Manager עם אירועים אסינכרונים רכיב סקריפט עם strategy = afterInteractive בשני הסקריפטים
לחצן 'מעקב' ב-Twitter ללא אירוע אסינכרוני או דחייה
הגדרת סקריפט ורכיב סקריפט לדוגמה 1 עם 2 סקריפטים.

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

השוואה בין רצועות שקפים שמראה שיפור ב-LCP

סקריפטים של צד שלישי בבלוג של Next.js

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

לפני אחרי
Google Tag Manager עם אירועים אסינכרונים רכיב סקריפט עם strategy = lazyonload לכל אחד מארבעת הסקריפטים
לחצן 'מעקב' ב-Twitter עם אסינכרוניזציה
לחצן הרשמה ל-YouTube ללא אירוע אסינכרוני או דחייה
לחצן 'למעקב' ב-LinkedIn ללא אירוע אסינכרוני או דחייה
הגדרת סקריפט ורכיב סקריפט לדוגמה 2 עם 4 סקריפטים.
סרטון שבו מוצגת התקדמות הטעינה של דף האינדקס עם רכיב הסקריפט וגם בלי. יש שיפור של 0.5 שניות ב-FCP עם רכיב הסקריפט.

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

מה צפוי לרכיב הסקריפט

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

שימוש ב-web workers

אפשר להשתמש בWeb workers כדי להריץ סקריפטים עצמאיים בשרשור רקע, וכך לפנות את השרשור הראשי לטיפול במשימות עיבוד של ממשק המשתמש ולשפר את הביצועים. Web Workers מתאימים במיוחד להעברת עומס העיבוד של JavaScript מה-thread הראשי, ולא לעבודה עם ממשק המשתמש. סקריפטים המשמשים לתמיכה בלקוחות או לשיווק, שבדרך כלל לא יוצרים אינטראקציה עם ממשק המשתמש, יכולים להתאים לביצוע בשרשור רקע. אפשר להשתמש בספרייה קלה של צד שלישי – PartyTown – כדי לבודד סקריפטים כאלה ב-web worker.

בהטמעה הנוכחית של רכיב הסקריפט של Next.js, מומלץ לדחות את הסקריפטים האלה בשרשור הראשי על ידי הגדרת האסטרטגיה ל-afterInteractive או ל-lazyOnload. בעתיד, אנחנו מתכננים להציג אפשרות אסטרטגיה חדשה, 'worker', שתאפשר ל-Next.js להשתמש ב-PartyTown או בפתרון בהתאמה אישית כדי להריץ סקריפטים ב-web workers. נשמח לקבל תגובות ממפתחים לגבי RFC הזה.

צמצום ה-CLS

תוכן מוטמע של צד שלישי, כמו מודעות, סרטונים או פידים של רשתות חברתיות, עלול לגרום לשינויים בפריסה במהלך טעינת פריטים בזמן אמת. המצב הזה משפיע על חוויית המשתמש ועל המדד Cumulative Layout Shift‏ (CLS) של הדף. כדי לצמצם את ה-CLS, אפשר לציין את הגודל של המאגר שבו ייטען התוכן המוטמע.

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

רכיבי Wrapper

בדרך כלל, התחביר ואסטרטגיית הטעינה של הוספת סקריפטים פופולריים של צד שלישי, כמו Google Analytics או Google Tag Manager ‏ (GTM), קבועים. אפשר גם להכניס אותם לקופסאות רכיבים נפרדות לכל סוג של סקריפט. רק קבוצה מינימלית של מאפיינים ספציפיים לאפליקציה (כמו מזהה מעקב) תהיה זמינה למפתחים. רכיבי העטיפה יעזרו למפתחים:

  1. קל יותר להם לכלול תגי סקריפט פופולריים.
  2. מוודאים שהמסגרת משתמשת באסטרטגיה האופטימלית ביותר.

סיכום

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

אימות חתימות

תודה לKara Erickson, ל-Janicklas Ralph, ל-Katie Hempenius, ל-Philip Walton, ל-Jeremy Wagner ול-Addy Osmani על המשוב ששלחו לגבי הפוסט הזה.