Progressive Web App: การทำงานแบบออฟไลน์

1. ยินดีต้อนรับ

ในแล็บนี้ คุณจะได้นำเว็บแอปพลิเคชันที่มีอยู่มาทำให้ทำงานแบบออฟไลน์ได้ ซึ่งเป็นแล็บแรกในชุดแล็บที่มาพร้อมกับเวิร์กช็อปProgressive Web App และยังมีแล็บอื่นๆ อีก 7 รายการในชุดนี้

สิ่งที่คุณจะได้เรียนรู้

  • เขียน Service Worker ด้วยตนเอง
  • เพิ่ม Service Worker ลงในเว็บแอปพลิเคชันที่มีอยู่
  • ใช้ Service Worker และ Cache Storage API เพื่อทำให้ทรัพยากรพร้อมใช้งานแบบออฟไลน์

สิ่งที่ควรทราบ

  • HTML พื้นฐาน และ JavaScript

สิ่งที่คุณต้องมี

2. เริ่มต้นใช้งาน

เริ่มต้นด้วยการโคลนหรือดาวน์โหลดโค้ดเริ่มต้นที่จำเป็นในการทำ Codelab นี้ให้เสร็จสมบูรณ์

หากโคลนที่เก็บ โปรดตรวจสอบว่าคุณอยู่ในสาขา starter ไฟล์ Zip จะมีโค้ดสำหรับสาขานั้นด้วย

โค้ดเบสนี้ต้องใช้ Node.js เวอร์ชัน 14 ขึ้นไป เมื่อมีโค้ดพร้อมแล้ว ให้เรียกใช้ npm ci จากบรรทัดคำสั่งในโฟลเดอร์ของโค้ดเพื่อติดตั้งการพึ่งพาที่คุณจะต้องใช้ทั้งหมด จากนั้นเรียกใช้ npm start เพื่อเริ่มเซิร์ฟเวอร์การพัฒนาสำหรับ Codelab

ไฟล์ README.md ของซอร์สโค้ดจะอธิบายไฟล์ที่เผยแพร่ทั้งหมด นอกจากนี้ ไฟล์สำคัญที่มีอยู่ซึ่งคุณจะได้ใช้ตลอด Codelab นี้มีดังนี้

ไฟล์สำคัญ

  • js/main.js - ไฟล์ JavaScript ของแอปพลิเคชันหลัก
  • service-worker.js - ไฟล์ Service Worker ของแอปพลิเคชัน

3. ทดสอบแบบออฟไลน์

ก่อนทำการเปลี่ยนแปลงใดๆ เรามาทดสอบเพื่อแสดงให้เห็นว่าปัจจุบันเว็บแอปไม่ทำงานแบบออฟไลน์ โดยทำอย่างใดอย่างหนึ่งต่อไปนี้ ปิดคอมพิวเตอร์ไม่ให้เชื่อมต่ออินเทอร์เน็ตแล้วโหลดเว็บแอปใหม่ หรือหากใช้ Chrome ให้ทำดังนี้

  1. เปิดเครื่องมือสำหรับนักพัฒนาเว็บใน Chrome
  2. เปลี่ยนไปที่แท็บแอปพลิเคชัน
  3. เปลี่ยนไปที่ส่วน Service Worker
  4. เลือกช่องทำเครื่องหมายออฟไลน์
  5. รีเฟรชหน้าโดยไม่ปิดเครื่องมือสำหรับนักพัฒนาเว็บใน Chrome

แท็บแอปพลิเคชันเครื่องมือสำหรับนักพัฒนาซอฟต์แวร์ Chrome Dev เปิดอยู่สำหรับ Service Worker โดยเลือกช่องทำเครื่องหมายออฟไลน์

เมื่อทดสอบเว็บไซต์แล้วและโหลดแบบออฟไลน์ไม่สำเร็จ ก็ถึงเวลาเพิ่มฟังก์ชันการทำงานแบบออนไลน์ ยกเลิกการเลือกช่องทำเครื่องหมายออฟไลน์แล้วไปยังขั้นตอนถัดไป

4. ทำให้ทำงานแบบออฟไลน์

ถึงเวลาเพิ่ม Service Worker พื้นฐานแล้ว ซึ่งจะเกิดขึ้นใน 2 ขั้นตอน ได้แก่ การลงทะเบียน Service Worker และการแคชทรัพยากร

ลงทะเบียน Service Worker

มีไฟล์ Service Worker ว่างเปล่าอยู่แล้ว ดังนั้นเพื่อให้แน่ใจว่าการเปลี่ยนแปลงจะแสดงขึ้น เรามาลงทะเบียนไฟล์ดังกล่าวในแอปพลิเคชันกัน โดยเพิ่มโค้ดต่อไปนี้ที่ด้านบนของ js/main.js

// Register the service worker
if ('serviceWorker' in navigator) {
  // Wait for the 'load' event to not block other work
  window.addEventListener('load', async () => {
    // Try to register the service worker.
    try {
      // Capture the registration for later use, if needed
      let reg;

      // Use ES Module version of our Service Worker in development
      if (import.meta.env?.DEV) {
        reg = await navigator.serviceWorker.register('/service-worker.js', {
          type: 'module',
        });
      } else {
        // In production, use the normal service worker registration
        reg = await navigator.serviceWorker.register('/service-worker.js');
      }

      console.log('Service worker registered! 😎', reg);
    } catch (err) {
      console.log('😥 Service worker registration failed: ', err);
    }
  });
}

คำอธิบาย

โค้ดนี้จะลงทะเบียนไฟล์ Service Worker service-worker.js ว่างเปล่าเมื่อหน้าเว็บโหลดแล้ว และเฉพาะในกรณีที่เว็บไซต์รองรับ Service Worker

แคชทรัพยากรล่วงหน้า

เพื่อให้เว็บแอปทำงานแบบออฟไลน์ได้ เบราว์เซอร์ต้องตอบสนองต่อคำขอเครือข่ายและเลือกเส้นทางที่จะส่งคำขอได้ โดยเพิ่มโค้ดต่อไปนี้ลงใน service-worker.js

// Choose a cache name
const cacheName = 'cache-v1';
// List the files to precache
const precacheResources = ['/', '/index.html', '/css/style.css', '/js/main.js', '/js/app/editor.js', '/js/lib/actions.js'];

// When the service worker is installing, open the cache and add the precache resources to it
self.addEventListener('install', (event) => {
  console.log('Service worker install event!');
  event.waitUntil(caches.open(cacheName).then((cache) => cache.addAll(precacheResources)));
});

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

// When there's an incoming fetch request, try and respond with a precached resource, otherwise fall back to the network
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);
    }),
  );
});

ตอนนี้กลับไปที่เบราว์เซอร์ ปิดแท็บแสดงตัวอย่าง แล้วเปิดขึ้นมาอีกครั้ง คุณควรเห็น console.log ที่สอดคล้องกับเหตุการณ์ต่างๆ ใน Service Worker

จากนั้นกลับไปออฟไลน์อีกครั้งแล้วรีเฟรชเว็บไซต์ คุณควรเห็นว่าเว็บไซต์โหลดได้แม้จะออฟไลน์อยู่ก็ตาม

คำอธิบาย

ในระหว่างเหตุการณ์การติดตั้งของ Service Worker ระบบจะเปิดแคชที่มีชื่อโดยใช้ Cache Storage API จากนั้นระบบจะโหลดไฟล์และเส้นทางที่ระบุไว้ใน precacheResources ลงในแคชโดยใช้เมธอด cache.addAll ซึ่งเรียกว่า การแคชล่วงหน้า เนื่องจากระบบจะแคชชุดไฟล์ล่วงหน้าในระหว่างการติดตั้ง ไม่ใช่แคชเมื่อจำเป็นหรือมีการขอ

เมื่อ Service Worker ควบคุมเว็บไซต์แล้ว ทรัพยากรที่ขอจะผ่าน Service Worker เหมือนกับพร็อกซี คำขอแต่ละรายการจะทริกเกอร์เหตุการณ์การดึงข้อมูล ซึ่งใน Service Worker นี้จะค้นหาแคชเพื่อหาทรัพยากรที่ตรงกัน หากพบทรัพยากรที่ตรงกัน ระบบจะตอบสนองด้วยทรัพยากรที่แคชไว้ หากไม่พบทรัพยากรที่ตรงกัน ระบบจะขอทรัพยากรตามปกติ

การแคชทรัพยากรช่วยให้แอปทำงานแบบออฟไลน์ได้โดยหลีกเลี่ยงคำขอเครือข่าย ตอนนี้แอปสามารถตอบสนองด้วยรหัสสถานะ 200 เมื่อออฟไลน์

5. ยินดีด้วย

คุณได้เรียนรู้วิธีทำให้เว็บแอปทำงานแบบออฟไลน์ได้โดยใช้ Service Worker และ Cache Storage API

Codelab ถัดไปในชุดนี้คือการทำงานกับ Workbox