פגז אפליקציה הוא הקוד המינימלי של HTML, CSS ו-JavaScript שמפעיל ממשק משתמש. מעטפת האפליקציה צריכה:
- טעינה מהירה
- נשמר במטמון.
- הצגת תוכן באופן דינמי
מעטפת אפליקציה היא הסוד לביצועים טובים ואמינים. אפשר לחשוב על המעטפת של האפליקציה כחבילת הקוד שתפרסמו בחנות אפליקציות אם הייתם מפתחים אפליקציה מקומית. זוהי החבילה שצריך כדי להתחיל, אבל יכול להיות שהיא לא כל הסיפור. ממשק המשתמש נשאר מקומי, והתוכן נשלף באופן דינמי דרך ממשק API.
רקע
במאמר של Alex Russell בנושא אפליקציות אינטרנט מתקדמות מוסבר איך אפליקציית אינטרנט יכולה להשתנות בהדרגה במהלך השימוש ובהסכמת המשתמש, כדי לספק חוויה דומה יותר לאפליקציה מקורית, עם תמיכה במצב אופליין, התראות ואפשרות להוסיף אותה למסך הבית. התשובה לשאלה הזו תלויה מאוד בהטבות הפונקציונליות והביצועים של service worker וביכולות האחסון שלהם במטמון. כך תוכלו להתמקד במהירות, ולספק לאפליקציות האינטרנט את אותה טעינה מיידית ואת העדכונים הקבועים שאתם רגילים לראות באפליקציות מקוריות.
כדי לנצל את מלוא היכולות האלה, אנחנו צריכים לחשוב בצורה חדשה על אתרים: ארכיטקטורת מעטפת האפליקציה.
עכשיו נלמד איך לבנות את האפליקציה באמצעות ארכיטקטורה של מעטפת אפליקציה עם תוספת של קובץ שירות. נבחן את העיבוד גם בצד הלקוח וגם בצד השרת, ונספק דוגמה מקצה לקצה שתוכלו לנסות כבר היום.
כדי להדגיש את הנקודה, בדוגמה הבאה מוצג הטעינה הראשונה של אפליקציה באמצעות הארכיטקטורה הזו. שימו לב להודעה 'האפליקציה מוכנה לשימוש אופליין' שמופיעה בתחתית המסך. אם עדכון למעטפת יהיה זמין מאוחר יותר, נוכל להודיע למשתמש לבצע רענון כדי לקבל את הגרסה החדשה.
מהם קובצי שירות (service workers)?
קובץ שירות (service worker) הוא סקריפט שפועל ברקע, בנפרד מדף האינטרנט. הוא מגיב לאירועים, כולל בקשות רשת שנשלחות מדפים שהוא מציג והתראות דחיפה מהשרת. משך החיים של קובץ שירות קצר בכוונה. הוא מתעורר כשמתקבל אירוע, ופועל רק כל עוד הוא צריך לעבד אותו.
בנוסף, ל-Service Workers יש קבוצה מוגבלת של ממשקי API בהשוואה ל-JavaScript בהקשר גלישה רגיל. זהו הסטנדרט לעובדים באינטרנט. ל-Service Worker אין גישה ל-DOM, אבל יש לו גישה לדברים כמו Cache API, והוא יכול לשלוח בקשות לרשת באמצעות Fetch API. אפשר גם להשתמש ב-IndexedDB API וב-postMessage() כדי לשמור נתונים ולשלוח הודעות בין ה-service worker לדפים שהוא שולט בהם. אירועי דחיפה שנשלחים מהשרת יכולים להפעיל את Notification API כדי להגביר את המעורבות של המשתמשים.
קובץ שירות יכול ליירט בקשות רשת שנשלחות מדף (שגורמות להפעלת אירוע אחזור בקובץ השירות) ולהחזיר תשובה שאוחזרה מהרשת, או שאוחזרה ממטמון מקומי, או אפילו שנוצרה באופן פרוגרמטי. למעשה, זה שרת proxy שניתן לתכנת בדפדפן. החלק המעניין הוא שלא משנה מאיפה מגיעה התגובה, מבחינת דף האינטרנט נראה כאילו לא הייתה מעורבות של קובץ השירות (service worker).
מידע נוסף על קובצי שירות זמין במאמר מבוא לקובצי שירות.
יתרונות הביצועים
שירותי עובדים הם כלי יעיל לשמירת נתונים במטמון אופליין, אבל הם גם מציעים שיפור משמעותי בביצועים באמצעות טעינה מיידית של ביקורים חוזרים באתר או באפליקציית האינטרנט. אפשר לשמור במטמון את מעטפת האפליקציה כדי שהיא תפעל אופליין, וליישב את התוכן שלה באמצעות JavaScript.
כך, בביקור חוזר, תוכלו לקבל פיקסלים משמעותיים במסך בלי הערוץ, גם אם התוכן שלכם מגיע ממנו בסופו של דבר. אפשר לחשוב על זה כעל הצגת סרגל כלים וכרטיסים מיד, ולאחר מכן טעינת שאר התוכן בהדרגה.
כדי לבדוק את הארכיטקטורה הזו במכשירים אמיתיים, הפעלנו את הדוגמה לקליפת האפליקציה ב-WebPageTest.org והצגנו את התוצאות בהמשך.
בדיקה 1: בדיקה באמצעות כבל עם Nexus 5 באמצעות Chrome Dev
התצוגה הראשונה של האפליקציה צריכה לאחזר את כל המשאבים מהרשת, והיא לא מציגה ציור משמעותי עד 1.2 שניות. בזכות האחסון במטמון של קובץ השירות, הביקור החוזר מסתיים בזמן טעינה של 0.5 שניות, עם ציור משמעותי.
בדיקה 2: בדיקה ב-3G באמצעות מכשיר Nexus 5 באמצעות Chrome Dev
אפשר גם לבדוק את הדוגמה שלנו בחיבור 3G איטי יותר. הפעם, זמן הצגת התוכן העיקרי (FMP) הוא 2.5 שניות בביקור הראשון. הטעינה המלאה של הדף נמשכת 7.1 שניות. בעזרת שמירת נתונים במטמון של שירות העבודה, הביקור החוזר מסתיים בזמן של 0.8 שניות, עם ציור משמעותי והשלמת הטעינה.
תצוגות אחרות מספרות סיפור דומה. אפשר להשוות ל3 השניות שנדרשות להצגת התוכן העיקרי (FMP) הראשונה בקליפת האפליקציה:
ל-0.9 שניות שנדרשות לטעינת אותו דף מהמטמון של קובץ השירות שלנו. משתמשי הקצה שלנו חוסכים יותר מ-2 שניות.
אפשר להשיג שיפורים דומים ואמינים בביצועים של האפליקציות שלכם באמצעות הארכיטקטורה של מעטפת האפליקציה.
האם שירותי ה-Worker מחייבים אותנו לחשוב מחדש על אופן המבנה של האפליקציות?
שימוש ב-Service Workers מחייב שינויים קלים בארכיטקטורה של האפליקציה. במקום לדחוס את כל האפליקציה למחרוזת HTML, כדאי לבצע פעולות בסגנון AJAX. כאן יש מעטפת (שתמיד שמורה במטמון ותמיד אפשר להפעיל אותה בלי הרשת) ותוכן שמתעדכן באופן קבוע ומנוהל בנפרד.
ההשלכות של הפיצול הזה משמעותיות. בביקור הראשון אפשר ליצור עיבוד (render) של תוכן בשרת ולהתקין את ה-service worker בלקוח. בביקורים הבאים, צריך רק לבקש נתונים.
מה לגבי שיפור הדרגתי?
נכון לעכשיו, לא כל הדפדפנים תומכים ב-Service Worker, אבל הארכיטקטורה של מעטפת התוכן של האפליקציה מבוססת על שיפור הדרגתי כדי להבטיח שלכולם תהיה גישה לתוכן. לדוגמה, ניקח את הפרויקט לדוגמה שלנו.
בהמשך אפשר לראות את הגרסה המלאה שעבר רינדור ב-Chrome, ב-Firefox Nightly וב-Safari. בצד הכי ימין מוצגת גרסת Safari שבה התוכן עובר עיבוד בשרת בלי עובד שירות. בצד שמאל מוצגות הגרסאות של Chrome ו-Firefox Nightly שמבוססות על service worker.
מתי כדאי להשתמש בארכיטקטורה הזו?
ארכיטקטורת מעטפת האפליקציה מתאימה במיוחד לאפליקציות ולאתרים דינמיים. אם האתר שלכם קטן וסטטי, סביר להניח שאתם לא צריכים מעטפת אפליקציה, ואפשר פשוט לשמור את האתר כולו במטמון בשלב oninstall
של קובץ השירות. מומלץ להשתמש בגישה שמתאימה ביותר לפרויקט שלכם. מספר מסגרות של JavaScript כבר מעודדות לפצל את הלוגיקה של האפליקציה מהתוכן, כך שקל יותר להחיל את התבנית הזו.
האם יש כבר אפליקציות ייצור שמשתמשות בדפוס הזה?
אפשר להשתמש בארכיטקטורה של פגז האפליקציה עם כמה שינויים בלבד בממשק המשתמש הכולל של האפליקציה. היא עבדה טוב באתרים גדולים כמו אפליקציית האינטרנט המתקדמת של Google בכנס I/O 2015 ו-Inbox של Google.
מעטפת אפליקציה אופליין היא שיפור משמעותי בביצועים, והיא ממחישה את עצמה היטב באפליקציית Wikipedia אופליין של ג'ייק ארקדיל ובאפליקציית האינטרנט המתקדמת של Flipkart Lite.
הסבר על הארכיטקטורה
במהלך חוויית הטעינה הראשונה, המטרה שלכם היא להציג תוכן משמעותי במסך של המשתמש במהירות האפשרית.
טעינת הדף הראשון וטעינת דפים אחרים
באופן כללי, ארכיטקטורת המעטפת של האפליקציה:
כדאי לתת עדיפות לטעינה הראשונית, אבל לאפשר ל-service worker לשמור במטמון את מעטפת האפליקציה, כדי שבביקורים חוזרים לא יהיה צורך לאחזר מחדש את המעטפת מהרשת.
טעינת שאר הפריטים באיטרציה או ברקע. אפשרות טובה אחת היא להשתמש באחסון במטמון לקריאה לתוכן דינמי.
לדוגמה, אפשר להשתמש בכלים של שירותי עבודה, כמו sw-precache, כדי לשמור במטמון ולעדכן בצורה מהימנה את שירות העבודה שמנהל את התוכן הסטטי. (מידע נוסף על sw-precache מופיע בהמשך).
כדי לעשות זאת:
השרת ישלח תוכן HTML שהלקוח יוכל ליצור ממנו פריימים, וישתמש בכותרות תפוגת תוקף של מטמון HTTP לטווח ארוך כדי להתחשב בדפדפנים ללא תמיכה ב-Service Worker. הוא יציג שמות של קובצי האחסון באמצעות גיבוב (hash) כדי לאפשר גם 'ניהול גרסאות' וגם עדכונים קלים בשלב מאוחר יותר במחזור החיים של האפליקציה.
דפים יכללו סגנונות CSS בקוד בתוך תג
<style>
במסמך<head>
כדי לספק צביעה ראשונית מהירה של מעטפת האפליקציה. כל דף יטמיע באופן אסינכרוני את קוד ה-JavaScript הנדרש לתצוגה הנוכחית. אי אפשר לטעון CSS באופן אסינכרוני, ולכן אנחנו יכולים לבקש סגנונות באמצעות JavaScript כי הוא כן אסינכרוני, ולא מבוסס-ניתוח וסינכרוני. אנחנו יכולים גם להשתמש ב-requestAnimationFrame()
כדי למנוע מקרים שבהם יכול להיות שתהיה לנו היטה מהירה במטמון, וכתוצאה מכך סגנונות יהיו חלק מנתיב העיבוד הקריטי בטעות.requestAnimationFrame()
מאלץ את המערכת לצייר את המסגרת הראשונה לפני שהסגנונות נטענים. אפשרות נוספת היא להשתמש בפרויקטים כמו loadCSS של Filament Group כדי לבקש CSS באופן אסינכרוני באמצעות JavaScript.Service worker יאחסן רשומה במטמון של מעטפת האפליקציה, כך שבביקורים חוזרים ניתן יהיה לטעון את המעטפת במלואה מהמטמון של ה-service worker, אלא אם יש עדכון זמין ברשת.
הטמעה מעשית
כתבנו דוגמה שפועלת באופן מלא באמצעות ארכיטקטורת מעטפת האפליקציה, JavaScript רגילה של ES2015 עבור הלקוח ו-Express.js עבור השרת. כמובן שאין שום דבר שמונע מכם להשתמש בסטאק משלכם בחלק של הלקוח או בחלק של השרת (למשל PHP, Ruby, Python).
מחזור החיים של קובץ השירות (service worker)
בפרויקט המעטפת של האפליקציה שלנו, אנחנו משתמשים ב-sw-precache, שמציע את מחזור החיים הבא של עובד השירות:
אירוע | פעולה |
---|---|
התקנה | שמירת המעטפת של האפליקציה ומשאבים אחרים של אפליקציות בדף יחיד במטמון. |
הפעלה | ניקוי מטמון ישן. |
הבא | להציג אפליקציית אינטרנט של דף יחיד לכתובות URL ולהשתמש במטמון לנכסים ולחלקים חלקיים מוגדרים מראש. שימוש ברשת לבקשות אחרות. |
ביטים של שרת
בארכיטקטורה הזו, רכיב בצד השרת (במקרה שלנו, שנכתב ב-Express) אמור להיות מסוגל לטפל בתוכן ובתצוגה בנפרד. אפשר להוסיף תוכן לפריסת HTML שמתקבלת ממנה עיבוד סטטי של הדף, או להציג אותו בנפרד ולטעון אותו באופן דינמי.
מובן שיכול להיות שההגדרה שלכם בצד השרת תהיה שונה מאוד מההגדרה שבה אנחנו משתמשים באפליקציית הדגמה שלנו. אפשר ליצור את התבנית הזו של אפליקציות אינטרנט ברוב הגדרות השרת, אבל כן נדרשת קצת תכנון מחדש. הגענו למסקנה שהמודל הבא עובד די טוב:
נקודות הקצה מוגדרות בשלושה חלקים של האפליקציה: כתובות ה-URL שגלויות למשתמשים (דף הבית/סמל אסימון כוכב), מעטפת האפליקציה (service worker) וחלקי ה-HTML.
לכל נקודת קצה יש בקר שמושך פריסה של מוטות צד, שבתורה יכולה למשוך תצוגות וחלקים חלקיים של מוטות צד. במילים פשוטות, תצוגות חלקיות הן תצוגות שמכילות קטעי HTML שמועברים לדף הסופי. הערה: לרוב קל יותר להעביר מסגרות JavaScript שמבצעות סנכרון נתונים מתקדם יותר לארכיטקטורה של פגודה של אפליקציה. בדרך כלל נעשה בהן שימוש בקישור נתונים ובסנכרון במקום בחלקים חלקיים.
בשלב הראשון, מוצג למשתמש דף סטטי עם תוכן. בדף הזה מתבצע רישום של קובץ שירות (אם הוא נתמך), שמאחסן במטמון את מעטפת האפליקציה ואת כל מה שהיא תלויה בו (CSS, JS וכו').
לאחר מכן, מעטפת האפליקציה תפעל כאפליקציית אינטרנט של דף יחיד, ותשתמש ב-JavaScript כדי להעביר את התוכן של כתובת URL ספציפית באמצעות XHR. הקריאות ל-XHR מתבצעות לנקודת קצה מסוג /partials* שמחזירה את הקטע הקטן של HTML, CSS ו-JS שנדרש כדי להציג את התוכן הזה. הערה: יש הרבה דרכים לעשות את זה, ו-XHR היא רק אחת מהן. אפליקציות מסוימות יציגו את הנתונים שלהן בקוד (אולי באמצעות JSON) לצורך הרינדור הראשוני, ולכן הן לא 'סטטיות' במובן של HTML שטוח.
בדפדפנים ללא תמיכה ב-service worker, תמיד צריך להציג חוויית שימוש חלופית. בדמו שלנו, אנחנו חוזרים לעיבוד סטטי בסיסי בצד השרת, אבל זו רק אחת מאפשרויות רבות. הצד של ה-service worker מספק הזדמנויות חדשות לשיפור הביצועים של אפליקציות בסגנון 'אפליקציה בדף יחיד' באמצעות מעטפת האפליקציה שנשמרה במטמון.
ניהול גרסאות של קבצים
אחת מהשאלות שעולות היא איך מטפלים בגרסת הקובץ ובעדכון שלו. האפשרות הזו ספציפית לאפליקציה, והאפשרויות הן:
קודם מהרשת, אחרת מהגרסה ששמורה במטמון.
רשת בלבד, ותיכשל אם המכשיר במצב אופליין.
לשמור את הגרסה הישנה במטמון ולעדכן מאוחר יותר.
לגבי מעטפת האפליקציה עצמה, צריך להשתמש בגישה של שמירת נתונים במטמון קודם כשמגדירים את ה-service worker. אם לא שומרים במטמון את מעטפת האפליקציה, סימן שלא הטמעתם את הארכיטקטורה בצורה נכונה.
כלים
אנחנו שומרים על מספר ספריות עזר של שירותי עבודה שבעזרתן קל יותר להגדיר את תהליך האחסון המוקדם של מעטפת האפליקציה או לטפל בדפוסי אחסון נפוצים.
שימוש ב-sw-precache למעטפת האפליקציה
שימוש ב-sw-precache כדי לשמור את מעטפת האפליקציה במטמון אמור לטפל בבעיות שקשורות לתיקוני קובץ, לשאלות לגבי התקנה/הפעלה ולתרחיש האחזור של מעטפת האפליקציה. מוסיפים את sw-precache לתהליך ה-build של האפליקציה ומשתמשים בתווים כלליים לחיפוש שניתן להגדיר כדי לאסוף את המשאבים הסטטיים. במקום ליצור את הסקריפט של קובץ השירות באופן ידני, אפשר לאפשר ל-sw-precache ליצור סקריפט שמנהל את המטמון בצורה בטוחה ויעילה באמצעות טיפול באחזור שמתחיל במטמון.
ביקורים ראשוניים באפליקציה מפעילים אחסון מראש של הקבוצה המלאה של המשאבים הנדרשים. החוויה הזו דומה לחוויה של התקנת אפליקציה רגילה מחנות אפליקציות. כשמשתמשים חוזרים לאפליקציה, רק המשאבים המעודכנים מורידים. בדמו שלנו, אנחנו מעדכנים את המשתמשים כשיש מעטפת חדשה באמצעות ההודעה 'עדכוני אפליקציה. צריך לרענן כדי לקבל את הגרסה החדשה". התבנית הזו היא דרך פשוטה להודיע למשתמשים שהם יכולים לרענן את הדף כדי לקבל את הגרסה העדכנית.
שימוש ב-sw-toolbox לשמירת נתונים במטמון בסביבת זמן הריצה
משתמשים ב-sw-toolbox כדי לשמור במטמון בזמן ריצה באמצעות אסטרטגיות שונות בהתאם למשאב:
cacheFirst לתמונות, יחד עם מטמון ייעודי בעל שם עם מדיניות תפוגה מותאמת אישית של N maxEntries.
networkFirst או המהירה ביותר לבקשות API, בהתאם לרמת העדכניות הרצויה של התוכן. האפשרות 'המהיר ביותר' עשויה להתאים, אבל אם יש פיד API ספציפי שמתעדכן לעיתים קרובות, כדאי להשתמש באפשרות 'networkFirst'.
סיכום
לארכיטקטורות של מעטפת אפליקציה יש כמה יתרונות, אבל הן הגיוניות רק לגבי סוגים מסוימים של אפליקציות. המודל עדיין חדש, וחשוב להעריך את המאמץ ואת יתרונות הביצועים הכוללים של הארכיטקטורה הזו.
בניסויים שלנו, נצלנו את האפשרות לשתף תבניות בין הלקוח לשרת כדי למזער את העבודה על בניית שתי שכבות של אפליקציות. כך אפשר להבטיח ששיפור הדרגתי עדיין יהיה תכונה ליבה.
אם אתם כבר שוקלים להשתמש בקובצי שירות באפליקציה, כדאי לעיין בארכיטקטורה ולבדוק אם היא מתאימה לפרויקטים שלכם.
תודה למבקרים שלנו: ג'ף פושניק (Jeff Posnick), פול לואיס (Paul Lewis), אלכס ראסל (Alex Russell), סת' תומפסון (Seth Thompson), רוב דודסון (Rob Dodson), טיילור סאבאג' (Taylor Savage) וג'ו מדלי (Joe Medley).