אופטימיזציה בזמן ההפעלה של JavaScript

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

רשת

Image for: רשת

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

זו יכולה להיות בעיה, כי סוג החיבור בפועל לרשת של המשתמש עשוי להיות לא 3G, ‏ 4G או Wi-Fi. אתם יכולים להיות מחוברים לרשת Wi-Fi בקפה, אבל להתחבר לנקודה לשיתוף אינטרנט (Hotspot) סלולרית במהירויות 2G.

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

  • שולחים רק את הקוד שהמשתמש צריך.
    • משתמשים בפיצול קוד כדי לפצל את ה-JavaScript לקטעים קריטיים ולא קריטיים. חבילות מודולים כמו webpack תומכות בחלוקת קוד.
    • טעינה איטית של קוד לא קריטי.
  • צמצום קוד
  • דחיסה
    • לכל הפחות, כדאי להשתמש ב-gzip כדי לדחוס משאבים מבוססי-טקסט.
    • מומלץ להשתמש ב-Brotli‏ ~q11. יחס הדחיסה של Brotli טוב יותר מזה של gzip. בעזרת הכלי, ב-CertSimple הצליחו לצמצם את גודל הבייטים של ה-JS המכווץ ב-17%, וב-LinkedIn הצליחו לצמצם את זמני הטעינה ב-4%.
  • הסרת קוד שלא בשימוש.
  • שמירת קוד במטמון כדי לצמצם את מספר הבקשות לרשת.
    • כדאי להשתמש באחסון במטמון של HTTP כדי לוודא שהדפדפנים מאחסנים את התשובות במטמון בצורה יעילה. כדי להימנע מהעברת בייטים שלא השתנו, כדאי לקבוע את משך החיים האופטימלי לסקריפטים (max-age) ולספק אסימוני אימות (ETag).
    • שימוש במטמון של קובץ שירות (service worker) יכול לשפר את עמידות הרשת של האפליקציה ולספק גישה מיידית לתכונות כמו מטמון הקוד של V8.
    • כדאי להשתמש במטמון לטווח ארוך כדי שלא תצטרכו לאחזר מחדש משאבים שלא השתנו. אם משתמשים ב-Webpack, אפשר לעיין במאמר גיבוב של שמות קבצים.

ניתוח/הידור

Image for: ניתוח/הידור

אחרי ההורדה, אחד מהעלויות הכבדות של JavaScript הוא הזמן שנדרש למנוע JS לנתח/לעבד את הקוד הזה. ב-Chrome DevTools, הניתוח וההדרכה הם חלק מהזמן הצהוב 'כתיבה ב-Script' בחלונית 'ביצועים'.

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

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

אבל למה זה חשוב?

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

עיבוד JavaScript בדפדפן יקר יותר מבייטים לכל תמונה או גופן אינטרנט בגודל שווה – Tom Dale

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

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

כשאנחנו מדברים על איטיות של ניתוח וקמפלקציה, חשוב להביא בחשבון את ההקשר – אנחנו מדברים כאן על טלפונים ניידים ממוצעים. משתמשים ממוצעים יכולים להשתמש בטלפונים עם מעבדים מרכזיים ומעבדי GPU איטיים, ללא מטמון L2/L3 ואולי אפילו עם מגבלת זיכרון.

יכולות הרשת והיכולות של המכשיר לא תמיד תואמות. משתמש עם חיבור Fiber מדהים לא בהכרח מקבל את המעבד הטוב ביותר לניתוח ולבדיקה של JavaScript שנשלח למכשיר שלו. זה נכון גם להפך… חיבור רשת גרוע אבל מעבד מהיר במיוחד. – Kristofer Baxter, LinkedIn

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

בתרשים הזה מודגשים זמני הניתוח של חבילת JavaScript בגודל 1MB (כ-250KB בפורמט GZIP) במכשירים נייחים ובמכשירים ניידים מסוגים שונים. כשבודקים את העלות של הניתוח, צריך ל��ביא בחשבון את ה��תונים ל��א ��חיסה.לדוגמה, קוד JavaScript דחוס בגודל של כ-250KB יתבטל הדחיסה שלו ויהפוך לקוד בגודל של כ-1MB.

מה קורה אם מדובר באתר בעולם האמיתי, כמו CNN.com?

ב-iPhone 8 מתקדם, ניתוח או הידור של ה-JS של CNN נמשכים כ-4 שניות, לעומת כ-13 שניות בטלפון ממוצע (Moto G4). הדבר יכול להשפיע באופן משמעותי על המהירות שבה משתמש יכול ליצור אינטראקציה מלאה עם האתר.

למעלה מוצגים זמני הניתוח של השוואת הביצועים של צ'יפ A11 Bionic של Apple ל-Snapdragon 617 בחומרה רגילה יותר של Android.

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

מערכת Google Analytics יכולה לספק תובנות לגבי הסוגים של מכשירים ניידים שבאמצעותם המשתמשים האמיתיים ניגשים לאתר שלכם. כך תוכלו להבין את המגבלות האמיתיות של המעבדים המרכזיים (CPU) או של המעבדים הגרפיים (GPU) שבהם הם פועלים.

האם אנחנו באמת שולחים יותר מדי JavaScript? אולי :)

בעזרת HTTP Archive (כ-500,000 אתרים מובילים) כדי לנתח את המצב של JavaScript בניידים, אנחנו יכולים לראות של-50% מהאתרים נדרשות יותר מ-14 שניות כדי להפוך לאינטראקטיביים. האתרים האלה משקיעים עד 4 שניות רק בניתוח ובהידור של JS.

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

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

זמן ביצוע

Image for: זמן ביצוע

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

אם הסקריפט פועל במשך יותר מ-50 אלפיות השנייה, הזמן עד לפעילות מלאה מתעכב בכל משך הזמן שנדרש להורדה, להדרכה ולביצוע של ה-JS – Alex Russell

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

עלויות אחרות

Image for: עלויות אחרות

JavaScript יכול להשפיע על ביצועי הדף בדרכים נוספות:

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

דפוסים להפחתת עלות העברת JavaScript

Image for: דפוסים להפחתת עלות העברת JavaScript

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

PRPL

PRPL (Push, Render, Pre-cache, Lazy-load) הוא תבנית שמבצעת אופטימיזציה לאינטראקטיביות באמצעות פיצול קוד ושימוש נרחב במטמון:

נציג לכם תמונה ויזואלית של ההשפעה האפשרית.

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

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

אתחול פרוגרסיבי

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

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

גישה טובה יותר עשויה להיות Bootstrapping הדרגתי. שליחת דף עם פונקציונליות מינימלית (הדף מורכב ��ק מ-HTML/JS/CSS הנדרשים למסלול הנוכחי). ככל שמגיעים יותר משאבים, האפליקציה יכולה לטעון אותם באיטרציה ולפתוח תכונות נוספות.

Progressive Bootstrapping מאת Paul Lewis

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

מסקנות

Image for: מסקנות

גודל ההעברה הוא קריטי לרשתות ברמה נמוכה. זמן הניתוח חשוב במכשירים עם מעבדים שמוגבלים ל-CPU. חשוב לשמור על ערכי ה-latency וה-throughput נמוכים.

צוותים שבחרו להשתמש בתקציבי ביצועים מחמירים הצליחו לצמצם את זמני ההעברה והניתוח/הקמפלור של JavaScript. מומלץ לקרוא את המאמר של Alex Russell בנושא Can You afford it?: תקציבי ביצועים באינטרנט בעולם האמיתי" לקבלת הנחיות לגבי תקציבים לנייד.

כדאי לחשוב כמה 'מרחב ראש' ב-JS ההחלטות הארכיטקטוניות שלנו יכולות להשאיר לנו לצורך לוגיקה של האפליקציה.

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

מידע נוסף

Image for: מידע נוסף