אופטימיזציה של קוד JavaScript של צד שלישי

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

  • דחיית טעינת הסקריפט

  • טעינה מדורגת של משאבים לא קריטיים

  • התחברות מראש למקורות נדרשים

האפליקציה לדוגמה הכלולה כוללת דף אינטרנט פשוט עם שלוש תכונות שמגיעות ממקורות צד שלישי:

  • הטמעת סרטון

  • ספריית נתונים להמחשת נתונים לעיבוד תרשים קו

  • ווידג'ט לשיתוף ברשתות חברתיות

צילום מסך של הדף עם הדגשה של משאבי צד שלישי.
משאבים של צד שלישי באפליקציה לדוגמה.

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

מדידת ביצועים

קודם כול פותחים את האפליקציה לדוגמה בתצוגת מסך מלא:

  1. לוחצים על רמיקס לעריכה כדי שיהיה אפשר לערוך את הפרויקט.
  2. כדי לראות תצוגה מקדימה של האתר, מקישים על View App ואז על Fullscreen מסך מלא.

אפשר להריץ בדיקת ביצועים של Lighthouse כדי לקבוע את רמת הביצועים הבסיסית:

  1. לוחצים על 'Control+Shift+J' (או 'Command+Option+J' ב-Mac) כדי לפתוח את כלי הפיתוח.
  2. לוחצים על הכרטיסייה Lighthouse.
  3. לוחצים על נייד.
  4. מסמנים את התיבה ביצועים. (אפשר למחוק את שאר התיבות בקטע 'ביקורות'.)
  5. לוחצים על Simulated Fast 3G, 4x CPU Slowdown.
  6. מסמנים את התיבה פינוי נפח אחסון.
  7. לוחצים על הרצת ביקורות.

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

צילום מסך של ביקורת Lighthouse שמציגה FCP באורך 2.4 שניות ושתי הזדמנויות: ביטול משאבים שחוסמים את העיבוד וחיבור מראש למקורות הנדרשים.

דחיית JavaScript של צד שלישי

הביקורת ביטול משאבים שחוסמים עיבוד זיהתה שניתן לחסוך זמן על ידי דחיית סקריפט שמגיע מ-d3js.org:

צילום מסך של 'הסרת ביקורת משאבים שחוסמים עיבוד' כאשר הסקריפט d3.v3.min.js מודגש.

D3.js היא ספריית JavaScript ליצירת תצוגות חזותיות של נתונים. הקובץ script.js באפליקציה לדוגמה משתמש בפונקציות כלי עזר מסוג D3 כדי ליצור את תרשים הקו של SVG ולצרף אותו לדף. סדר הפעולות כאן חשוב: script.js צריך לפעול אחרי ניתוח המסמך וספריית D3 נטענת, ולכן הוא נכלל ממש לפני תג </body> הסוגר ב-index.html.

עם זאת, הסקריפט D3 נכלל ב-<head> של הדף, שחוסם את הניתוח של מסמך השאר:

צילום מסך של index.html עם תג סקריפט מודגש בראש.

שתי תכונות קסם יכולות לבטל את חסימת המנתח כשמוסיפים אותן לתג הסקריפט:

  • async מוודא שהסקריפטים יורדים ברקע ומופעלים בהזדמנות הראשונה לאחר סיום ההורדה.

  • defer מוודא שהסקריפטים יורדים ברקע ומופעלים לאחר שהניתוח מסתיים לחלוטין.

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

שלב 1: טוענים את הסקריפט באופן אסינכרוני באמצעות המאפיין defer

בשורה 17 ב-index.html, מוסיפים את המאפיין defer לרכיב <script>:

<script src="https://d3js.org/d3.v3.min.js" defer></script>

שלב 2: מוודאים שסדר הפעולות נכון

עכשיו ש-D3 נדחה, הפונקציה script.js תפעל לפני ש-D3 מוכן, ולכן תתקבל שגיאה.

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

<script src="https://d3js.org/d3.v3.min.js" defer></script>
<script src="./script.js" defer></script>

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

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

באפליקציה לדוגמה יש סרטון YouTube שמוטמע ב-iframe. כדי לבדוק כמה בקשות הדף שולח ומגיעות מה-iframe המוטמע של YouTube:

  1. כדי לראות תצוגה מקדימה של האתר, מקישים על View App ואז על Fullscreen מסך מלא.
  2. לוחצים על 'Control+Shift+J' (או 'Command+Option+J' ב-Mac) כדי לפתוח את כלי הפיתוח.
  3. לוחצים על הכרטיסייה רשתות.
  4. מסמנים את התיבה Disable cache (השבתת המטמון).
  5. בוחרים באפשרות Fast 3G בתפריט הנפתח Throttling.
  6. לטעון מחדש את הדף.

צילום מסך של החלונית DevTools Network.

בחלונית רשת, נחשף שהדף שלח 28 בקשות בסך הכול והועבר כמעט 1MB של משאבים דחוסים.

כדי לזהות את הבקשות שנשלחו על ידי iframe ב-YouTube, צריך לחפש את מזהה הווידאו 6lfaiXM6waw בעמודה יוזם. כדי לקבץ את כל הבקשות לפי דומיין:

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

  • בתפריט הנפתח, בוחרים את העמודה דומיינים.

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

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

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

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

שלב 1: מניעת טעינה ראשונית של הסרטון

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

<iframe width="560" height="315" data-src="https://www.youtube.com/embed/lS9D6w1GzGY" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

data-src הוא מאפיין נתונים שמאפשר לאחסן מידע נוסף על רכיבי HTML רגילים. למאפיין נתונים אפשר לתת כל שם, כל עוד הוא מתחיל ב-"data-".

iframe ללא src פשוט לא ייטען.

שלב 2: משתמשים ב-Intersection Exploreer כדי לטעון את הסרטון בהדרגה

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

כדי להתחיל, צרו קובץ חדש ותנו לו את השם lazy-load.js:

  • לוחצים על קובץ חדש ונותנים לו שם.
  • לוחצים על Add This File (הוספת הקובץ הזה).

מוסיפים את תג הסקריפט לכותרת המסמך:

 <script src="/lazy-load.js" defer></script>

ב-lazy-load.js, יוצרים IntersectionObserver חדש ומעבירים לו פונקציית קריאה חוזרת כדי להריץ אותה:

// create a new Intersection Observer
let observer = new IntersectionObserver(callback);

עכשיו צריך לתת ל-observer רכיב יעד לצפייה (במקרה הזה, ה-iframe של הסרטון) על ידי העברתו כארגומנט ב-method observe:

// the element that you want to watch
const element = document.querySelector('iframe');

// register the element with the observe method
observer.observe(element);

הפונקציה callback מקבלת רשימה של אובייקטים IntersectionObserverEntry ואת האובייקט IntersectionObserver עצמו. כל רשומה מכילה אלמנט target ומאפיינים שמתארים את המידות שלו, את המיקום שלו, את השעה שבה הוא נכנס לאזור התצוגה ועוד. אחד המאפיינים של IntersectionObserverEntry הוא isIntersecting – ערך בוליאני שווה ל-true כשהאלמנט נכנס לאזור התצוגה.

בדוגמה הזו, target הוא iframe. isIntersecting שווה true כאשר target מוזן באזור התצוגה. כדי לראות את זה בפעולה, צריך להחליף את callback בפונקציה הבאה:

let observer = new IntersectionObserver(callback);
let observer = new IntersectionObserver(function(entries, observer) {
    entries.forEach(entry => {
      console.log(entry.target);
      console.log(entry.isIntersecting);
    });
  });
  1. כדי לראות תצוגה מקדימה של האתר, מקישים על View App ואז על Fullscreen מסך מלא.
  2. לוחצים על 'Control+Shift+J' (או 'Command+Option+J' ב-Mac) כדי לפתוח את כלי הפיתוח.
  3. לוחצים על הכרטיסייה מסוף.

נסו לגלול למעלה ולמטה. אתם אמורים לראות את הערך של השינוי isIntersecting ואת רכיב היעד שנרשם במסוף.

כדי לטעון את הסרטון כשהמשתמש גולל למיקום שלו, צריך להשתמש ב-isIntersecting כתנאי להרצת פונקציית loadElement, שמקבלת את הערך מה-data-src של האלמנט iframe ומגדירה אותו כמאפיין src של האלמנט iframe. ההחלפה הזו גורמת לטעינת הסרטון. לאחר מכן, אחרי שהסרטון נטען, מפעילים את השיטה unobserve ב-observer כדי להפסיק לצפות ברכיב היעד:

let observer = new IntersectionObserver(function (entries, observer) {
  entries.forEach(entry => {
    console.log(entry.target);
    console.log(entry.isIntersecting);
  });
});
    if (entry.isIntersecting) {
      // do this when the element enters the viewport
      loadElement(entry.target);
      // stop watching
      observer.unobserve(entry.target);
    }
  });
});

function loadElement(element) {
  const src = element.getAttribute('data-src');
  element.src = src;
}

שלב 3: הערכה מחדש של הביצועים

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

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

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

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

הוספת המאפיין rel=preconnect לקישור מורה לדפדפן ליצור חיבור לדומיין לפני שליחת הבקשה למשאב. מומלץ להשתמש במאפיין הזה במקורות שמספקים משאבים שאתם בטוחים שנדרשים לדף.

הביקורת של Lighthouse שהרצת בשלב הראשון הוצעה בקישור מראש למקורות נדרשים. הבדיקה הזו מאפשרת לך לחסוך כ-400 אלפיות השנייה על ידי יצירת חיבורים מוקדמים אל staticxx.facebook.com ו-youtube.com:

צריך להתחבר מראש לבדיקת המקורות הנדרשים כשהדומיין staticxx.facebook.com מודגש.

מאחר שהסרטון ב-YouTube נטען עכשיו בצורה מדורגת, נשאר רק staticxx.facebook.com, המקור של ווידג'ט השיתוף של המדיה החברתית. יצירת חיבור מוקדם לדומיין הזה פשוטה כמו הוספת תג <link> ל-<head> של המסמך:

  <link rel="preconnect" href="https://staticxx.facebook.com">

הערכה מחדש של הביצועים

זה מצב הדף אחרי האופטימיזציה. פועלים לפי השלבים שבקטע Measure performance (מדידת הביצועים) ב-Codelab כדי להריץ ביקורת נוספת של Lighthouse.

ביקורת בנושא Lighthouse שכללה FCP שנייה אחת וציון ביצועים של 99.