קובצי שירות (service worker) ומודל המעטפת של האפליקציה

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

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

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

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

  • ביצועים מהימנים ועקביים בביקורים חוזרים. בביקור הראשון באפליקציה שלא מותקן בה קובץ שירות (service worker), יש לטעון מהרשת את תגי העיצוב של האפליקציה ואת הנכסים המשויכים אליה כדי שה-Service Worker יוכל להכניס אותם למטמון שלה. עם זאת, ביקורים חוזרים ישלפו את מעטפת האפליקציה מהמטמון, כלומר, הטעינה והעיבוד יתבצעו באופן מיידי.
  • גישה אמינה לפונקציונליות בתרחישים אופליין. לפעמים הגישה לאינטרנט חלשה או נעדרת לגמרי, והמסך החשש 'לא הצלחנו למצוא את האתר' מופנה כלפי מעלה. מודל מעטפת האפליקציה מטפל בכך על ידי תגובה לכל בקשת ניווט עם תגי העיצוב של מעטפת האפליקציה מתוך המטמון. גם אם מישהו יבקר בכתובת URL באפליקציית האינטרנט שלא ביקר בו אף פעם, מעטפת האפליקציה תוצג מהמטמון, ואפשר לאכלס אותה בתוכן שימושי.

מתי צריך להשתמש במודל המעטפת של האפליקציה

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

אם תיאור זה מתאים לפרויקט שלכם, ואתם רוצים להוסיף קובץ שירות (service worker) כדי לשפר את האמינות והביצועים שלו, מעטפת האפליקציה צריכה:

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

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

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

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

האיזון הנכון תלוי באפליקציה שלכם. מעטפת האפליקציה של האפליקציה Trained To Thrill של ג'ייק ארצ'יבלד כוללת כותרת עם לחצן רענון שמושך תוכן חדש מ-Flickr.

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

תגי העיצוב של מעטפת האפליקציה ישתנו בין פרויקט לפרויקט, אבל הנה דוגמה אחת לקובץ index.html שמספק את התבנית הסטנדרטית של האפליקציה:

​​<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>
      Application Shell Example
    </title>
    <link rel="manifest" href="/manifest.json">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <link rel="stylesheet" type="text/css" href="styles/global.css">
  </head>
  <body>
    <header class="header">
      <!-- Application header -->
      <h1 class="header__title">Application Shell Example</h1>
    </header>

    <nav class="nav">
      <!-- Navigation items -->
    </nav>

    <main id="app">
      <!-- Where the application content populates -->
    </main>

    <div class="loader">
      <!-- Spinner/content placeholders -->
    </div>

    <!-- Critical application shell logic -->
    <script src="app.js"></script>

    <!-- Service worker registration script -->
    <script>
      if ('serviceWorker' in navigator) {
        // Register a service worker after the load event
        window.addEventListener('load', () => {
          navigator.serviceWorker.register('/sw.js');
        });
      }
    </script>
  </body>
</html>

בכל פעם שאתם בונים מעטפת אפליקציה לפרויקט, צריכות להיות לה המאפיינים הבאים:

  • ה-HTML צריך לכלול אזורים מבודדים בבירור עבור רכיבים נפרדים בממשק המשתמש. בדוגמה שלמעלה, הפריטים האלה כוללים את הכותרת, הניווט, אזור התוכן הראשי והמקום ל'ספינר' של טעינה, שמופיע רק כאשר התוכן נטען.
  • ה-JavaScript וה-CSS הראשוניים שנטענים עבור מעטפת האפליקציה צריכים להיות מינימליים ולהיות קשורים רק לפונקציונליות של מעטפת האפליקציה עצמה ולא לתוכן. כך אפשר להבטיח שהמעטפת של האפליקציה תעובד מהר ככל האפשר, ולצמצם את העבודה על ה-thread הראשי עד שהתוכן יופיע.
  • סקריפט מוטבע שרושם קובץ שירות (service worker).

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

מעטפת האפליקציה מועברת במטמון

מעטפת האפליקציה והנכסים הנדרשים שלה הם הפריטים ש-Service Worker צריך לשמור מראש במטמון מיד בזמן ההתקנה. בהנחה של מעטפת אפליקציה כמו הדוגמה שלמעלה, בואו נראה כיצד ניתן להשיג זאת בדוגמה בסיסית של תיבת עבודה באמצעות workbox-build:

// build-sw.js
import {generateSW} from 'workbox-build';

// Where the generated service worker will be written to:
const swDest = './dist/sw.js';

generateSW({
  swDest,
  globDirectory: './dist',
  globPatterns: [
    // The necessary CSS and JS for the app shell
    '**/*.js',
    '**/*.css',
    // The app shell itself
    'shell.html'
  ],
  // All navigations for URLs not precached will use this HTML
  navigateFallback: 'shell.html'
}).then(({count, size}) => {
  console.log(`Generated ${swDest}, which precaches ${count} assets totaling ${size} bytes.`);
});

התצורה ששמורה ב-build-sw.js מייבאת את ה-CSS וה-JavaScript של האפליקציה, כולל קובץ תגי העיצוב של מעטפת האפליקציה שנמצא ב-shell.html. הסקריפט מופעל עם צומת, באופן הבא:

node build-sw.js

קובץ השירות (service worker) שנוצר נכתב אל ./dist/sw.js, ובסיום הוא ירשום את ההודעה הבאה:

Generated ./dist/sw.js, which precaches 5 assets totaling 44375 bytes.

כשהדף נטען, ה-Service Worker שומר מראש את סימון מעטפת האפליקציה ואת יחסי התלות שלו:

צילום מסך של חלונית הרשת בכלי הפיתוח של Chrome, שמוצגת בו רשימת נכסים שהורדו מהרשת. נכסים שנשמרו מראש על ידי קובץ השירות (service worker) נבדלים מנכסים אחרים עם גלגל שיניים מופיע בצד ימין בשורה. בזמן ההתקנה של Service Worker, חלק מקובצי JavaScript ו-CSS נשמרים מראש במטמון.
ה-Service Worker שומר מראש את יחסי התלות של מעטפת האפליקציה בזמן ההתקנה. הבקשות מראש במטמון הן שתי השורות האחרונות, וסמל גלגל השיניים לצד הבקשה מציין שה-Service Worker טיפל בבקשה.

ניתן לשמור מראש את ה-HTML, CSS ו-JavaScript של מעטפת האפליקציה בכל תהליך עבודה, כולל פרויקטים המשתמשים בחבילות. ככל שתתקדמו בתיעוד, תלמדו איך להשתמש ישירות ב-Workbox כדי להגדיר את שרשרת הכלים שלכם ליצירת קובץ שירות (service worker) המתאים ביותר לפרויקט שלכם, בין אם זה SPA.

סיכום

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