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

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

Leena Sohoni
Leena Sohoni

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

רכיב הסקריפט של 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" />

למאפיין האסטרטגיה יכולים להיות שלושה ערכים.

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

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

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

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

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

מדידת ההשפעה

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

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

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

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

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

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

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

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

לפני אחרי
‫Google Tag Manager עם async רכיב סקריפט עם strategy = lazyonload לכל אחד מארבעת הסקריפטים
כפתור Follow של Twitter עם async
כפתור ההרשמה ל-YouTube בלי async או defer
כפתור Follow של LinkedIn בלי async או defer
הגדרת הסקריפט והרכיב של הסקריפט להדגמה 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 Worker. נשמח לקבל תגובות ממפתחים על ה-RFC הזה.

מזעור ה-CLS

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

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

רכיבי Wrapper

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

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

סיכום

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

תודות

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