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

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

רשת

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

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

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

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

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

ניתוח/הידור

אחרי ההורדה, אחת מהעלויות הגבוהות ביותר של JavaScript היא הזמן שלוקח למנוע JS לנתח/לקמפל את הקוד הזה. בכלי הפיתוח ל-Chrome, הניתוח וההידור הם חלק מהזמן הצהוב של "Scripting" בחלונית Performance.

ALT_TEXT_HERE

בכרטיסיות Bottom-Up ו-Call Tree מוצגים זמני הניתוח/הקימפול המדויקים:

ALT_TEXT_HERE
חלונית הביצועים של כלי הפיתוח ל-Chrome‏ > Bottom-Up. כשהתכונה Runtime Call Stats של V8 מופעלת, אנחנו יכולים לראות את הזמן שהושקע בשלבים כמו Parse ו-Compile

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

ALT_TEXT_HERE

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

בייט לבייט, לדפדפן יקר יותר לעבד JavaScript מאשר תמונה או גופן אינטרנט בגודל שווה ערך — טום דייל

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

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

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

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

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

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

מה לגבי אתר אמיתי, כמו CNN.com?

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

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

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

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

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

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

ALT_TEXT_HERE

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

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

זמן הביצוע

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

ALT_TEXT_HERE

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

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

עלויות אחרות

‫JavaScript יכול להשפיע על ביצועי הדף בדרכים אחרות:

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

תבניות להפחתת עלות ההעברה של JavaScript

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

PRPL

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

ALT_TEXT_HERE

בואו נראה איך זה משפיע.

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

ALT_TEXT_HERE

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

אתחול הדרגתי

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

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

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

ALT_TEXT_HERE
Progressive Bootstrapping מאת פול לואיס

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

מסקנות

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

צוותים רבים הצליחו להפחית את זמני ההעברה והניתוח/משך הזמן לקימפול של JavaScript על ידי הגדרת תקציבי ביצועים מחמירים. במאמר של אלכס ראסל, "Can You Afford It?: Real-world Web Performance Budgets" כדי לקבל הנחיות לגבי תקציבים לנייד.

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

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

מידע נוסף