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

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

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

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

למרות שמעטפת האפליקציה נטענת מהר, בתנאי שהרשת זמינה לפחות במידה מסוימת, קובץ שירות (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 כך:

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) לבין נכסים אחרים שיש להם גלגל שיניים בצד שמאל בשורה. חלק מקובצי JavaScript ו-CSS נשמרים מראש על ידי Service Worker בזמן ההתקנה.
ה-Service Worker שומר מראש את יחסי התלות של מעטפת האפליקציה בזמן ההתקנה. בקשות ההעברה למטמון הן שתי השורות האחרונות, וסמל גלגל השיניים שמופיע לצד הבקשה מציין שה-Service Worker טיפל בבקשה.

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

סיכום

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