ביצוע קל ופשוט של ביצועי האינטרנט - מהדורת 2018 של Google I/O

בכנס Google IO 2018 הצגנו סיכום של כלים, ספריות ושיטות אופטימיזציה שיעזרו לכם לשפר את ביצועי האתרים בקלות. כאן נסביר עליהם באמצעות האפליקציה Oodles Theater. אנחנו גם מדברים על הניסויים שלנו בטעינה חזותית ועל היוזמה החדשה Guess.js.

Addy Osmani
Addy Osmani
Ewa Gasperowicz

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

הצורך בביצועים

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

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

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

פירמידה של היררכיית UX
איור 1. עד כמה המהירות חשובה למשתמשים? (Speed Matters, Vol. 3)

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

Lighthouse – בסיס לתהליך העבודה לשיפור הביצועים

Lighthouse הוא חלק מכלי הפיתוח של Chrome שמאפשר לבצע ביקורת של האתר ולקבל טיפים לשיפורו.

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

בדיקות חדשות של Lighthouse
איור 2. ביקורות חדשות של Lighthouse

נראה איך אפשר להפיק מהם תועלת באמצעות דוגמה מעשית: אפליקציית Oodles Theater. זוהי אפליקציית אינטרנט קטנה לדגמה, שבה אפשר לנסות כמה מה-Google Doodles האינטראקטיביים האהובים עלינו ואפילו לשחק במשחק או שניים.

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

דוח Lighthouse לאפליקציית Oodles
איור 3. דוח Lighthouse לאפליקציית Oodles

הביצועים הראשוניים של האפליקציה שלנו, כפי שמוצגים בדוח Lighthouse, היו די גרועים. ברשת 3G, המשתמש היה צריך להמתין 15 שניות עד לציור הראשון של האובייקטים המשמעותיים, או עד שהאפליקציה הפכה לאינטראקטיבית. ב-Lighthouse נמצאו המון בעיות באתר שלנו, ודירוג הביצועים הכולל של 23 משקף בדיוק את זה.

הדף היה במשקל של כ-3.4MB – היינו צריכים להוציא ממנו קצת שומן.

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

הזדמנויות לאופטימיזציה של הביצועים

הסרת משאבים מיותרים

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

היתרונות של הקטנה
איור 4. אופטימיזציה ודחיסת קובצי JavaScript ו-CSS

ב-Lighthouse מודגשת ההזדמנות הזו בבדיקה של CSS ו-JavaScript ללא דחיסה. השתמשנו ב-webpack בתהליך ה-build, ולכן כדי לבצע הקטנה השתמשנו פשוט בPlugin של Uglify JS.

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

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

השתמשנו ב-Firebase Hosting כדי לארח את הקוד שלנו, ו-Firebase מפעילה gzipping כברירת מחדל, כך שפשוט בזכות האירוח של הקוד שלנו ב-CDN סביר קיבלנו את זה בחינם.

gzip היא דרך פופולרית מאוד לדחיסת נתונים, אבל גם מנגנונים אחרים כמו Zopfli ו-Brotli צוברים תאוצה. רוב הדפדפנים תומכים ב-Brotli, ואפשר להשתמש בקובץ בינארי כדי לדחוס מראש את הנכסים לפני ששולחים אותם לשרת.

שימוש במדיניות מטמון יעילה

השלב הבא היה לוודא שלא נשלח משאבים פעמיים אם אין צורך בכך.

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

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

הסרת קוד שלא בשימוש

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

בדיקת כיסוי הקוד בכלי הפיתוח
איור 5. בדיקת הכיסוי של הקוד

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

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

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

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

אם נוריד את המתאם של MVC, הגודל של הסגנונות יירד ל-10KB
איור 6. אם נוריד את המתאם של MVC, הגודל של הסגנונות יירד ל-10KB!

בעקבות זאת, הגודל של חבילת ה-CSS שלנו ירד פי 20, וזה די טוב עבור התחייבות קטנה באורך שורה אחת.

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

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

הקוד שלנו לא היה בשימוש ב-95% מהמקרים – עדיין יש את ה-5% האלה איפשהו. נראה שאחד מהרכיבים שלנו עדיין השתמש בסגנונות מהספרייה הזו – החצים הקטנים בפס ההזזה של ה-doodle. אבל מכיוון שהיא הייתה קטנה כל כך, הצלחנו פשוט לשלב את הסגנונות האלה חזרה בלחצנים באופן ידני.

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

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

הימנעות מטען שימושי עצום ברשת

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

בעזרת הביקורת עומס יתר של נתוני משתמש ברשת, מערכת Lighthouse הצליחה לזהות בעיה בחלק מעומסי הנתונים ברשת שלנו.

זיהוי של עומסי נתונים עצומים ברשת
איור 8. זיהוי עומסי נתונים עצומים ברשת

כאן ראינו שיש לנו יותר מ-3MB של קוד שנשלח למטה – כמות גדולה למדי, במיוחד בנייד.

בחלק העליון של הרשימה, Lighthouse הדגיש שיש לנו חבילת JavaScript של ספק שמכילה 2MB של קוד לא דחוס. זו גם בעיה ש-webpack מדגיש.

כמו שאומרים: הבקשה המהירה ביותר היא זו שלא נשלחת.

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

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

ביקורת של חבילות JavaScript
איור 9. ביקורת של חבילות JavaScript

התחלנו עם webpack bundle analyzer, שסיפק לנו מידע על כך שכללנו יחסי תלות שנקראים unicode, שהכילו 1.6MB של JavaScript שעבר ניתוח, כלומר כמות גדולה למדי.

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

לאחר מכן עברנו לכלי אחר, BundlePhobia. זהו כלי שמאפשר להזין את השם של כל חבילה ב-NPM ולראות בפועל מהו הגודל המשוער שלה אחרי דחיסה מינימלית ודחיסה ב-Gzip. מצאנו חלופה נהדרת למודול ה-slug שבו השתמשנו, שמשקלו רק 2.2KB, ולכן החלפנו אותו.

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

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

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

קיצור זמן האתחול של JavaScript באמצעות פיצול קוד

למרות שלמשא המועמס (payload) גדול ברשת יכולה להיות השפעה גדולה על האפליקציה שלנו, יש עוד דבר שיכול להשפיע מאוד, וזה JavaScript.

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

כך דפדפן מעבד JavaScript.

עיבוד JavaScript
איור 10. עיבוד JavaScript

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

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

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

זמן האתחול של JavaScript
איור 11. בדיקת זמן האתחול של JavaScript

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

אחת מהשיטות לפתרון הבעיה הזו היא שימוש בפיצול קוד.

פיצול קוד הוא כמו פיצה

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

אפשר להחיל את פיצול הקוד ברמת המסלול או ברמת הרכיב. הוא עובד מצוין עם React ו-React Loadable,‏ Vue.js,‏ Angular,‏ Polymer,‏ Preact וספריות רבות אחרות.

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

פיצול קוד באמצעות ייבוא דינמי
איור 13. פיצול קוד באמצעות ייבוא דינמי

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

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

אם אתם משתמשים ב-webpack, כדאי להיעזר במושגים כמו חלוקת קוד, לבדוק רעיונות כמו tree shaking ולעיין במאגר webpack-libs-optimizations כדי לקבל כמה רעיונות לקיצור גודל הספרייה.

בצע אופטימיזציה לתמונות

בדיחה על ביצועי טעינת תמונות

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

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

בדיקות של תמונות
איור 14. בדיקות תמונות ב-Lighthouse

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

לסבב אופטימיזציה חד-פעמי, אפשר להשתמש בכלים חזותיים כמו ImageOptim או XNConvert.

גישה אוטומטית יותר היא להוסיף שלב של אופטימיזציה של תמונות לתהליך ה-build, באמצעות ספריות כמו imagemin.

כך תוכלו לוודא שהתמונות שיתווספו בעתיד יותאמו אוטומטית. שירותי CDN מסוימים, כמו Akamai או פתרונות של צד שלישי כמו Cloudinary,‏ Fastly או Uploadcare, מציעים פתרונות מקיפים לאופטימיזציה של תמונות. כך תוכלו גם לארח את התמונות שלכם בשירותים האלה.

אם אתם לא רוצים לעשות זאת בגלל העלות או בעיות זמן אחזור, יש פרויקטים כמו Thumbor או Imageflow שמציעים חלופות באירוח עצמי.

לפני ואחרי האופטימיזציה
איור 15. לפני ואחרי האופטימיזציה

קובץ ה-PNG של הרקע סומן ב-webpack כגדול, ובצדק. אחרי ששינינו את הגודל שלו בהתאם למסך הצפייה והעברנו אותו דרך ImageOptim, הגודל ירד ל-100KB, וזה גודל מקובל.

חזרה על הפעולה הזו בכמה תמונות באתר אפשרה לנו להפחית באופן משמעותי את משקל הדף הכולל.

שימוש בפורמט הנכון לתוכן מונפש

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

באפליקציית Oodle, השתמשנו ב-GIF כסצנת פתיחה בדף הבית. לפי Lighthouse, אפשר לחסוך יותר מ-7MB על ידי מעבר לפורמט וידאו יעיל יותר. הקליפ שלנו היה במשקל של כ-7.3MB, הרבה יותר מדי לאתר סביר, אז במקום זאת הפכנו אותו לרכיב וידאו עם שני קובצי מקור – קובץ MP4 וקובץ WebM כדי לאפשר תמיכה רחבה יותר בדפדפנים.

החלפת קובצי GIF מונפשים בסרטון
איור 16. החלפת קובצי GIF באנימציה בסרטון

השתמשנו בכלי FFmpeg כדי להמיר את קובץ ה-GIF של האנימציה לקובץ MP4. פורמט WebM מציע חיסכון גדול עוד יותר – ממשק ה-API של ImageOptim יכול לבצע המרה כזו בשבילכם.

ffmpeg -i animation.gif -b:v 0 -crf 40 -vf scale=600:-1 video.mp4

הצלחנו לחסוך יותר מ-80% ממשקל האריזה הכולל בזכות ההמרה הזו. כך הצלחנו להקטין את הגודל ל-1MB בערך.

עם זאת, 1MB הוא משאב גדול להעברה באינטרנט, במיוחד למשתמש עם רוחב פס מוגבל. למזלנו, הצלחנו להשתמש ב-Effective Type API כדי להבין שהם משתמשים ברוחב פס איטי, ובמקום זאת שלחנו להם קובץ JPEG קטן בהרבה.

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

if (navigator.connection.effectiveType) { ... }

זה גורם לירידה קלה בחוויית השימוש, אבל לפחות אפשר להשתמש באתר בחיבור איטי.

טעינה מדורגת של תמונות מחוץ למסך

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

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

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

<!-- Import library -->
import lazysizes from 'lazysizes'  <!-- or -->
<script src="lazysizes.min.js"></script>

<!-- Use it -->

<img data-src="image.jpg" class="lazyload"/>
<img class="lazyload"
    data-sizes="auto"
    data-src="image2.jpg"
    data-srcset="image1.jpg 300w,
    image2.jpg 600w,
    image3.jpg 900w"/>

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

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

איך לעזור לדפדפן לספק משאבים קריטיים מוקדם

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

אפשרות שימושית היא שאנחנו, ככותבי הדף, נודיע לדפדפן מה באמת חשוב לנו. למרבה המזל, בשנים האחרונות ספקי הדפדפנים הוסיפו כמה תכונות שיעזרו לנו בכך, למשל רמזים לגבי משאבים כמו link rel=preconnect,‏ preload או prefetch.

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

נראה איך Lighthouse מנחה אותנו להשתמש ביעילות בחלק מהתכונות האלה.

הדבר הראשון ש-Lighthouse ממליץ לנו לעשות הוא להימנע מכמה נסיעות הלוך ושוב יקרות לכל מקור.

הימנעות מכמה נסיעות הלוך ושוב יקרות לכל מקור
איור 17. הימנעות מכמה נסיעות הלוך ושוב יקרות לכל מקור

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

בעזרת הקישור rel preconnect, אנחנו יכולים להסוות ביעילות את זמן האחזור הזה.

במיוחד במקרים כמו Google Fonts, שבהם קובצי ה-CSS של גווני הגופן מתארחים ב-googleapis.com ומשאבי הגופן מתארחים ב-Gstatic, יכול להיות לכך השפעה משמעותית. אז החלטנו להחיל את האופטימיזציה הזו, וכך הצלחנו לחסוך כמה מאות אלפיות השנייה.

ההצעה הבאה של Lighthouse היא לטעון מראש בקשות למפתחות.

טעינה מראש של בקשות למפתחות
איור 18. טעינה מראש של בקשות למפתחות

<link rel=preload> הוא כלי יעיל מאוד. הוא מודיע לדפדפן על צורך במשאב כחלק מהניווט הנוכחי, ומנסה לגרום לדפדפן לאחזר אותו בהקדם האפשרי.

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

טעינת גופן אינטרנט מראש נראית כך: מציינים את rel=preload, מעבירים את as עם סוג הגופן ואז מציינים את סוג הגופן שרוצים לטעון, למשל woff2.

ההשפעה על הדף שלכם עשויה להיות דרמטית.

ההשפעה של טעינה מראש של משאבים
איור 19. ההשפעה של טעינה מראש של משאבים

בדרך כלל, בלי להשתמש ב-link rel preload, אם גופנים לדפדפן הם קריטיים לדף, הדפדפן צריך קודם לאחזר את ה-HTML, לנתח את ה-CSS, ואז, הרבה יותר מאוחר, הוא יתחיל לאחזר את הגופנים לדפדפן.

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

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

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

במקרה שלנו, הכלי Google Web Fonts Helper עזר לנו מאוד להוריד אופליין חלק מהגופנים האלה ולהגדיר אותם באופן מקומי. מומלץ לבדוק את הכלי הזה.

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

ניסיוני: רמזים לגבי רמת העדיפות

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

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

זוהי תכונה חדשה שמאפשרת להעביר לרשת הדפדפן רמז לגבי מידת החשיבות של משאב. היא חושפת מאפיין חדש – importance (חשיבות) – עם הערכים low (נמוך), high (גבוה) או auto (אוטומטי).

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

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

הגדרת העדיפות של התוכן שגלוי בהתחלה
איור 21. הגדרת תעדוף לתוכן שגלוי בהתחלה

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

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

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

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

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

הימנעות משימוש בטקסט בלתי נראה בזמן הטעינה של פונטים מסוג webfont
איור 22. הימנעו משימוש בטקסט בלתי נראה בזמן הטעינה של פונטים מסוג webfont

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

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

    @font-face {
      font-family: 'Montserrat';
      font-style: normal;
      font-display: swap;
      font-weight: 400;
      src: local('Montserrat Regular'), local('Montserrat-Regular'),
          /* Chrome 26+, Opera 23+, Firefox 39+ */
          url('montserrat-v12-latin-regular.woff2') format('woff2'),
            /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
          url('montserrat-v12-latin-regular.woff') format('woff');
    }

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

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

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

תוצאה של תצוגת גופן
איור 23. תוצאת תצוגת הגופן

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

יש הרבה תכונות בפלטפורמת האינטרנט שאפשר להשתמש בהן כדי לבצע אופטימיזציה של חוויית הטעינה של הגופנים, אבל כדאי גם לעיין במאגר Web Font Recipes של Zach Leatherman, כי הוא נהדר.

הפחתת מספר הסקריפטים שחוסמים את העיבוד

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

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

צמצום ההזדמנויות של גיליונות סגנונות לחסימת עיבוד
איור 24. צמצום ההזדמנויות לשימוש בגיליונות סגנונות שגורמים לחסימת הרינדור

הורדה ועיבוד של גיליונות סגנונות חיצוניים מונעים ממערכת הרינדור שלנו להתקדם.

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

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

במקרה שלנו, השתמשנו במודול NPM שנקרא Critical כדי להטמיע את התוכן הקריטי ב-index.html במהלך שלב ה-build.

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

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

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

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

התוצאה

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

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

ביצועים חזויים – חוויית משתמש מבוססת-נתונים

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

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

יש לנו נתונים זמינים שיעזרו לנו לשפר את האופטימיזציה שלנו כבר היום. באמצעות Google Analytics Reporting API אנחנו יכולים לבדוק את הדף הבא ברשימה של הדפים המובילים ואת אחוזי היציאה מכל כתובת URL באתר, וכך להסיק אילו משאבים כדאי לתת להם עדיפות.

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

חבילות מבוססות-נתונים לאפליקציות אינטרנט
איור 25. חבילות מבוססות-נתונים לאפליקציות אינטרנט

כדי להקל על הניסויים האלה, אנחנו שמחים להכריז על יוזמה חדשה שנקראת Guess.js.

Guess.js
איור 26. Guess.js

Guess.js הוא פרויקט שמתמקד בחוויית משתמש מבוססת-נתונים לאינטרנט. אנחנו מקווים שהדוח הזה יניע אתכם לבדוק את האפשרות להשתמש בנתונים כדי לשפר את ביצועי האתר, וגם מעבר לכך. כל הקוד הוא בקוד פתוח וזמין ב-GitHub כבר היום. הכלי הזה נוצר בשיתוף פעולה עם קהילת הקוד הפתוח על ידי Minko Gechev,‏ Kyle Matthews מ-Gatsby,‏ Katie Hempenius ועוד כמה אנשים.

כדאי לנסות את Guess.js ולספר לנו מה חשבתם.

סיכום

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

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

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

תודה מיוחדת ל-Ward Peeters, ‏ Minko Gechev, ‏ Kyle Mathews, ‏ Katie Hempenius, ‏ Dom Farolino,‏ Yoav Weiss, ‏ Susie Lu, ‏ Yusuke Utsunomiya, ‏ Tom Ankers, ‏ Lighthouse ו-Google Doodles.