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

Addy Osmani
Addy Osmani

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

רשת

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

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

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

אפשר לצמצם את עלות ההעברה ברשת של JavaScript באמצעות:

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

ניתוח/הידור

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

ALT_TEXT_HERE

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

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

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

ALT_TEXT_HERE

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

בייט לבייט, העיבוד ב-JavaScript יקר יותר מבחינת הדפדפן מאשר תמונה בגודל דומה או גופן אינטרנט — Tom Dale

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

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

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

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

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

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

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

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

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

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

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

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

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

ALT_TEXT_HERE

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

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

זמן ביצוע

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

ALT_TEXT_HERE

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

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

עלויות אחרות

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

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

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

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

PRPL (PRPL)

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

ALT_TEXT_HERE

בואו נראה את ההשפעה שיכולה להיות לכך.

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

ALT_TEXT_HERE

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

אתחול מתקדם (PWA)

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

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

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

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

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

מסקנות

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

הצוותים הצליחו אימוץ תקציבי ביצועים קפדניים כדי ששידור ה-JavaScript וזמני הניתוח/הידור שלהם נמוכים. קרא את "Can You Afford It?" של אלכס ראסל: התקציבים של הביצועים באינטרנט בעולם האמיתי" להנחיות בנושא תקציבים לנייד.

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

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

מידע נוסף