התחלה מהירה במצב אופליין

מעבדת הקוד הזו היא חלק מקורס ההדרכה של Progressive Web Apps, שפותח על ידי צוות ההדרכה של Google Developers. אם תשלימו את הקוד של השיעורים השונים ברצף, תוכלו להפיק את המקסימום מהקורס הזה.

לפרטים מלאים על הקורס, מומלץ לעיין בסקירה הכללית של Progressive Web Apps.

מבוא

בשיעור ה-Lab הזה תשתמשו ב-Lighthouse כדי לבדוק אתר לפי הסטנדרטים של Progressive Web App (PWA). תוכלו גם להוסיף פונקציונליות במצב אופליין באמצעות ה-API של קובץ השירות (service worker).

מה תלמדו

  • איך מבצעים ביקורת על אתרים באמצעות Lighthouse
  • כיצד להוסיף יכולות אופליין לאפליקציה

מה חשוב לדעת

  • HTML בסיסי, CSS ו-JavaScript
  • היכרות עם הבטחות של ES2015

מה צריך להכין

  • מחשב עם גישה למסוף/מעטפת
  • חיבור לאינטרנט
  • דפדפן Chrome (לשימוש ב-Lighthouse)
  • עורך טקסט
  • אופציונלי: Chrome במכשיר Android

מורידים או משכפלים את מאגר pwa-training-labs מ-github ומתקינים את גרסת LTS של Node.js, אם צריך.

עוברים לספרייה offline-quickstart-lab/app/ ופותחים שרת פיתוח מקומי:

cd offline-quickstart-lab/app
npm install
node server.js

אפשר לסיים את השרת בכל שלב עם Ctrl-c.

פותחים את הדפדפן ועוברים אל localhost:8081/. אתם אמורים לראות שהאתר הוא דף אינטרנט פשוט וסטטי.

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

פותחים את התיקייה offline-quickstart-lab/app/ בעורך הטקסט המועדף עליכם. התיקייה app/ היא המקום שבו תיצרו את שיעור ה-Lab.

התיקייה הזו מכילה:

  • תיקייה אחת (images/) מכילה תמונות לדוגמה
  • styles/main.css הוא גיליון הסגנונות הראשי
  • index.html הוא דף ה-HTML הראשי של האתר לדוגמה שלנו
  • package-lock.json ו-package.json עוקבים אחר תלויות באפליקציה (התלויות היחידות במקרה זה הן עבור שרת הפיתוח המקומי)
  • server.js הוא שרת פיתוח מקומי לבדיקה
  • service-worker.js הוא קובץ קובץ שירות (כרגע ריק)

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

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

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

הסבר

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

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

הקטע Progressive Web App אמור להיראות כך:

הדוח כולל ציונים ומדדים בחמש קטגוריות:

  • Progressive Web App
  • ביצועים
  • נגישות
  • שיטות מומלצות
  • אופטימיזציה עבור מנועי חיפוש

כפי שאפשר לראות, האפליקציה שלנו מקבלת ציון נמוך בקטגוריה Progressive Web App (PWA). בואו נשפר את הציון שלנו!

מקדישים רגע לעיון בקטע PWA של הדוח ומגלים מה חסר.

רישום של קובץ שירות (service worker)

אחד מהכשלים המפורטים בדוח הוא שלא רשום אף קובץ שירות (service worker). יש לנו כרגע קובץ קובץ שירות (service worker) ריק ב-app/service-worker.js.

יש להוסיף את הסקריפט הבא לתחתית של index.html, ממש לפני התג </body>:

<script>
if ('serviceWorker' in navigator) {
  window.addEventListener('load', function() {
    navigator.serviceWorker.register('service-worker.js')
      .then(reg => {
        console.log('Service worker registered! 😎', reg);
      })
      .catch(err => {
        console.log('😥 Service worker registration failed: ', err);
      });
  });
}
</script>

הסבר

הקוד רושם את קובץ ה-service worker של sw.js אחרי שהדף נטען. עם זאת, קובץ ה-service worker הנוכחי ריק ולא ייעשה דבר. בוא נוסיף קוד שירות בשלב הבא.

משאבי מטמון מראש

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

צריך להוסיף את הקוד הבא לקובץ קובץ השירות (sw.js):

const cacheName = 'cache-v1';
const precacheResources = [
  '/',
  'index.html',
  'styles/main.css',
  'images/space1.jpg',
  'images/space2.jpg',
  'images/space3.jpg'
];

self.addEventListener('install', event => {
  console.log('Service worker install event!');
  event.waitUntil(
    caches.open(cacheName)
      .then(cache => {
        return cache.addAll(precacheResources);
      })
  );
});

self.addEventListener('activate', event => {
  console.log('Service worker activate event!');
});

self.addEventListener('fetch', event => {
  console.log('Fetch intercepted for:', event.request.url);
  event.respondWith(caches.match(event.request)
    .then(cachedResponse => {
        if (cachedResponse) {
          return cachedResponse;
        }
        return fetch(event.request);
      })
    );
});

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

  • רשום
  • מותקנת
  • הופעל

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

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

הערה: ייתכן שתופיע שגיאת מסוף המציינת כי לא ניתן לאחזר את קובץ השירות (service worker): An unknown error occurred when fetching the script. service-worker.js Failed to load resource: net::ERR_CONNECTION_REFUSED. שגיאה זו מוצגת כי הדפדפן לא הצליח לשלוף את הסקריפט של קובץ ה-service worker (כי האתר במצב אופליין), אבל זה צפוי כי אנחנו לא יכולים להשתמש ב-worker (service worker) כדי לשמור את עצמו במטמון. אחרת, הדפדפן של המשתמש נתקע עם אותו קובץ שירות לתמיד.

הסבר

לאחר שה-worker של השירות נרשם על ידי סקריפט הרישום ב-index.html, אירוע ה-service worker install מתרחש. במהלך אירוע זה, event listener של install פותח מטמון בעל שם, ושומר במטמון את הקבצים שצוינו בשיטת cache.addAll. השלב הזה נקרא "precaching&&; הוא מתרחש במהלך האירוע install, שהוא בדרך כלל הפעם הראשונה שבה משתמש מבקר באתר.

לאחר התקנה של קובץ שירות (service worker), ואם קובץ שירות (service worker) אחר לא שולט כרגע בדף, ה-worker החדש (service worker) הוא "activateed" ( event listener של activate מופעל ב-service worker) והוא מתחיל לשלוט בדף.

כשנשלחת בקשה לקבל משאבים דרך דף (service worker) שולט בו, הבקשות עוברות דרך קובץ השירות (service worker), כמו שרת proxy של רשת. בכל אירוע מופעל אירוע fetch. ב-service worker שלנו, event listener של fetch מחפש את המטמון ומגיב עם המשאב השמור אם הוא זמין. אם המשאב לא נשמר במטמון, תישלח בקשה למשאב כרגיל.

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

הערה: לא ניתן להשתמש באירוע ההפעלה של שום דבר מלבד התחברות בדוגמה זו. האירוע נכלל כדי לעזור לנפות באגים הקשורים לבעיות במחזור החיים של קובץ השירות (service worker).

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

הפעלה מחדש של שרת הפיתוח ב-node server.js ורענון האתר. לאחר מכן, פותחים שוב את הכרטיסייה ביקורות בכלי למפתחים ומפעילים מחדש את ביקורת Lighthouse על ידי בחירה באפשרות ביקורת חדשה (סימן הפלוס בפינה הימנית העליונה). כאשר הבדיקה תסתיים, אתם אמורים לראות שציון ה-PWA שלנו טוב יותר באופן משמעותי, אבל עדיין ניתן לשפר את הציון שלנו בקטע הבא.

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

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

יצירת קובץ מניפסט

יוצרים קובץ ב-app/ שנקרא manifest.json ומוסיפים את הקוד הבא:

{
  "name": "Space Missions",
  "short_name": "Space Missions",
  "lang": "en-US",
  "start_url": "/index.html",
  "display": "standalone",
  "theme_color": "#FF9800",
  "background_color": "#FF9800",
  "icons": [
    {
      "src": "images/touch/icon-128x128.png",
      "sizes": "128x128"
    },
    {
      "src": "images/touch/icon-192x192.png",
      "sizes": "192x192"
    },
    {
      "src": "images/touch/icon-256x256.png",
      "sizes": "256x256"
    },
    {
      "src": "images/touch/icon-384x384.png",
      "sizes": "384x384"
    },
    {
      "src": "images/touch/icon-512x512.png",
      "sizes": "512x512"
    }
  ]
}

התמונות שצוינו במניפסט כבר זמינות באפליקציה.

לאחר מכן יש להוסיף את ה-HTML הבא לחלק התחתון של התג <head> ב-index.html:

<link rel="manifest" href="manifest.json">

<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="application-name" content="Space Missions">
<meta name="apple-mobile-web-app-title" content="Space Missions">
<meta name="theme-color" content="#FF9800">
<meta name="msapplication-navbutton-color" content="#FF9800">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="msapplication-starturl" content="/index.html">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

<link rel="icon" sizes="128x128" href="/images/touch/icon-128x128.png">
<link rel="apple-touch-icon" sizes="128x128" href="/images/touch/icon-128x128.png">
<link rel="icon" sizes="192x192" href="icon-192x192.png">
<link rel="apple-touch-icon" sizes="192x192" href="/images/touch/icon-192x192.png">
<link rel="icon" sizes="256x256" href="/images/touch/icon-256x256.png">
<link rel="apple-touch-icon" sizes="256x256" href="/images/touch/icon-256x256.png">
<link rel="icon" sizes="384x384" href="/images/touch/icon-384x384.png">
<link rel="apple-touch-icon" sizes="384x384" href="/images/touch/icon-384x384.png">
<link rel="icon" sizes="512x512" href="/images/touch/icon-512x512.png">
<link rel="apple-touch-icon" sizes="512x512" href="/images/touch/icon-512x512.png">

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

הסבר

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

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

היינו צריכים לנקות את נתוני האתר כדי להסיר את הגרסה הישנה שנשמרה במטמון של index.html (כי הגרסה הזו לא הכילה את קישור המניפסט). כדאי להריץ עוד ביקורת ב-Lighthouse ולראות עד כמה ציון ה-PWA השתפר.

מתבצעת הפעלה של בקשת ההתקנה

השלב הבא בהתקנת האפליקציה הוא להציג למשתמשים את בקשת ההתקנה. Chrome 67 ביקש משתמשים באופן אוטומטי, אך החל מגרסה 68 של Chrome, יש להפעיל את בקשת ההתקנה באופן פרוגרמטי בתגובה לתנועת משתמש.

מוסיפים &לחצן '&& נראה אפליקציה' בחלק העליון של index.html (מיד אחרי התג <main>) עם הקוד הבא:

<section id="installBanner" class="banner">
    <button id="installBtn">Install app</button>
</section>

לאחר מכן, מעצבים את הבאנר על ידי הוספת הסגנונות הבאים ל-styles/main.css:

.banner {
  align-content: center;
  display: none;
  justify-content: center;
  width: 100%;
}

שומרים את הקובץ. לבסוף, מוסיפים את תג הסקריפט הבא ל-index.html:

  <script>
    let deferredPrompt;
    window.addEventListener('beforeinstallprompt', event => {

      // Prevent Chrome 67 and earlier from automatically showing the prompt
      event.preventDefault();

      // Stash the event so it can be triggered later.
      deferredPrompt = event;

      // Attach the install prompt to a user gesture
      document.querySelector('#installBtn').addEventListener('click', event => {

        // Show the prompt
        deferredPrompt.prompt();

        // Wait for the user to respond to the prompt
        deferredPrompt.userChoice
          .then((choiceResult) => {
            if (choiceResult.outcome === 'accepted') {
              console.log('User accepted the A2HS prompt');
            } else {
              console.log('User dismissed the A2HS prompt');
            }
            deferredPrompt = null;
          });
      });

      // Update UI notify the user they can add to home screen
      document.querySelector('#installBanner').style.display = 'flex';
    });
  </script>

שומרים את הקובץ. פותחים את האפליקציה ב-Chrome במכשיר Android באמצעות ניפוי באגים מרחוק. כשהדף נטען, הלחצן "התקנת האפליקציה&quot מופיע לוחצים על הלחצן והחלון 'הוספה למסך הבית' אמור להופיע. פועלים לפי ההוראות כדי להתקין את האפליקציה במכשיר. לאחר ההתקנה, אמורה להיות לך אפשרות לפתוח את אפליקציית האינטרנט במצב עצמאי (מחוץ לדפדפן) על ידי הקשה על סמל מסך הבית החדש שנוצר.

הסבר

קוד ה-HTML ו-amp; מוסיפים את באנר ולחצן מוסתרים שבהם אנחנו יכולים להשתמש כדי לאפשר למשתמשים להפעיל את בקשת ההתקנה.

אחרי שהאירוע beforeinstallprompt יופעל, אנחנו מונעים את חוויית ברירת המחדל (בדפדפן Chrome 67 או גרסאות מוקדמות יותר, המשתמשים מתבקשים להתקין את האפליקציה באופן אוטומטי) ומתעדים את beforeinstallevent במשתנה deferredPrompt הגלובלי. לאחר מכן, הלחצן "התקנת האפליקציה" הוגדר להצגת ההודעה בשיטה prompt() של beforeinstallevent. לאחר שהמשתמש בוחר (להתקנת או לא) ההבטחה של userChoice נפתרת עם בחירת המשתמש (outcome). לבסוף, אנו מציגים את לחצן ההתקנה לאחר שהכול מוכן.

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

משאבים נוספים

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

כדי לראות את כל שיעורי ה-Lab בקורס ההכשרה ל-PWA, יש לעיין ב-Welcome Lab לקורס/