ชีวิตของ Service Worker

การที่จะรู้ว่าผู้ปฏิบัติงานบริการกำลังทำอะไรอยู่นั้นเป็นเรื่องยากหากไม่เข้าใจวงจรชีวิตของผู้ใช้ การทำงานภายในของพวกเขาดูไม่ชัดเจน แม้จะไม่มีกฎเกณฑ์ก็ตาม อย่าลืมว่าระบบได้กำหนดลักษณะการทำงานของโปรแกรมทำงานของบริการไว้อย่างดี เช่นเดียวกับ API อื่นๆ ของเบราว์เซอร์ ที่กำหนดและทำให้แอปพลิเคชันแบบออฟไลน์เป็นไปได้ ในขณะเดียวกันก็อำนวยความสะดวกในการอัปเดตโดยไม่รบกวนประสบการณ์ของผู้ใช้

ก่อนเข้าสู่ Workbox คุณต้องเข้าใจวงจรการทำงานของโปรแกรมทำงานของบริการเพื่อให้สิ่งที่ Workbox ทำได้อย่างเหมาะสม

การให้คำจำกัดความ

ก่อนเข้าสู่วงจรชีวิตของโปรแกรมทำงานของบริการ คุณควรกำหนดคำบางคำเกี่ยวกับการทำงานของวงจรนั้น

การควบคุมและขอบเขต

แนวคิดเกี่ยวกับการควบคุมมีความสำคัญอย่างยิ่งต่อการทำความเข้าใจวิธีการทำงานของโปรแกรมทำงานของบริการ หน้าที่โปรแกรมทำงานของบริการระบุว่าควบคุมได้คือหน้าที่อนุญาตให้โปรแกรมทำงานของบริการสกัดกั้นคำขอเครือข่ายในนามของตนเองได้ มี Service Worker และทำงานให้กับหน้าเว็บในขอบเขตที่กำหนดได้

ขอบเขต

ขอบเขตของ Service Worker จะกำหนดจากตำแหน่งของผู้ใช้ในเว็บเซิร์ฟเวอร์ หากโปรแกรมทำงานของบริการทำงานในหน้าที่ /subdir/index.html และอยู่ที่ /subdir/sw.js ขอบเขตของโปรแกรมทำงานของบริการคือ /subdir/ หากต้องการดูแนวคิดของขอบเขตในการใช้งานจริง ลองดูตัวอย่างนี้

  1. นำทางไปยัง https://service-worker-scope-viewer.glitch.me/subdir/index.html. ข้อความจะปรากฏขึ้นเพื่อแจ้งว่าไม่มี Service Worker กำลังควบคุมหน้านี้อยู่ แต่หน้าดังกล่าวจะมีการลงทะเบียน Service Worker จาก https://service-worker-scope-viewer.glitch.me/subdir/sw.js
  2. โหลดหน้าเว็บซ้ำ เนื่องจากมีการลงทะเบียน Service Worker แล้วและมีการใช้งานอยู่ ก็ทำการควบคุมหน้าอยู่ แบบฟอร์มที่มีขอบเขตของโปรแกรมทำงานของบริการ สถานะปัจจุบัน และ URL จะปรากฏ หมายเหตุ: การโหลดหน้าซ้ำไม่มีความเกี่ยวข้องใดๆ กับขอบเขต แต่เป็นวงจรของ Service Worker ซึ่งจะอธิบายในภายหลัง
  3. ไปที่ https://service-worker-scope-viewer.glitch.me/index.html แม้ว่าจะมีการลงทะเบียน Service Worker ในต้นทางนี้แล้ว ยังมีข้อความแจ้งว่าไม่มี Service Worker อยู่ในขณะนี้ เนื่องจากหน้านี้ไม่ได้อยู่ในขอบเขตของ Service Worker ที่ลงทะเบียน

ขอบเขตจะจำกัดหน้าที่ควบคุมของ Service Worker ในตัวอย่างนี้ หมายความว่า Service Worker ที่โหลดจาก /subdir/sw.js จะควบคุมได้เฉพาะหน้าที่อยู่ใน /subdir/ หรือแผนผังย่อยเท่านั้น

วิธีการข้างต้นคือวิธีการทำงานของการกำหนดขอบเขตโดยค่าเริ่มต้น แต่ขอบเขตสูงสุดที่อนุญาต คุณสามารถลบล้างได้โดยการตั้งค่า ส่วนหัวการตอบกลับ Service-Worker-Allowed รวมถึงการส่ง ตัวเลือก scope เป็นเมธอด register

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

ลูกค้า

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

วงจรของโปรแกรมทำงานของบริการใหม่

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

การลงทะเบียน

การลงทะเบียนเป็นขั้นตอนเริ่มต้นของวงจรการทำงานของโปรแกรมทำงานของบริการ

<!-- In index.html, for example: -->
<script>
  // Don't register the service worker
  // until the page has fully loaded
  window.addEventListener('load', () => {
    // Is service worker available?
    if ('serviceWorker' in navigator) {
      navigator.serviceWorker.register('/sw.js').then(() => {
        console.log('Service worker registered!');
      }).catch((error) => {
        console.warn('Error registering service worker:');
        console.warn(error);
      });
    }
  });
</script>

โค้ดนี้จะทำงานในเทรดหลักและดำเนินการต่อไปนี้

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

สิ่งสำคัญที่ควรทำความเข้าใจมีดังนี้

  • Service Worker คือ ใช้ได้เฉพาะใน HTTPS หรือ localhost
  • หากเนื้อหาของโปรแกรมทำงานของบริการมีข้อผิดพลาดทางไวยากรณ์ ลงทะเบียนไม่สำเร็จและระบบจะทิ้ง Service Worker
  • โปรดทราบ: Service Worker ทำงานภายในขอบเขตที่กำหนด ในที่นี้ ขอบเขตคือต้นทางทั้งหมด เนื่องจากโหลดจากไดเรกทอรีราก
  • เมื่อเริ่มการลงทะเบียน สถานะของ Service Worker จะตั้งค่าเป็น 'installing'

เมื่อการลงทะเบียนเสร็จสิ้น การติดตั้งจะเริ่มขึ้น

การติดตั้ง

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

// /sw.js
self.addEventListener('install', (event) => {
  const cacheKey = 'MyFancyCacheName_v1';

  event.waitUntil(caches.open(cacheKey).then((cache) => {
    // Add all the assets in the array to the 'MyFancyCacheName_v1'
    // `Cache` instance for later use.
    return cache.addAll([
      '/css/global.bc7b80b7.css',
      '/css/home.fe5d0b23.css',
      '/js/home.d3cc4ba4.js',
      '/js/jquery.43ca4933.js'
    ]);
  }));
});

การดำเนินการนี้จะสร้างอินสแตนซ์ Cache ใหม่และแคชเนื้อหาล่วงหน้า เรามีโอกาสมากมายที่จะได้พูดถึงการแคชล่วงหน้าในภายหลัง ตอนนี้เรามาดูที่บทบาทของ event.waitUntil event.waitUntil รับคำมั่นสัญญา และรอจนกว่าสัญญาดังกล่าวจะได้รับการแก้ไข ในตัวอย่างนี้ สัญญาดังกล่าวจะทำสิ่งที่ไม่พร้อมกัน 2 อย่าง

  1. สร้างอินสแตนซ์ Cache ใหม่ชื่อ 'MyFancyCache_v1'
  2. หลังจากสร้างแคชแล้ว อาร์เรย์ของ URL เนื้อหาได้รับการแคชล่วงหน้าโดยใช้แบบไม่พร้อมกัน addAll วิธี

การติดตั้งล้มเหลวหากมีการส่งคำสัญญาไปยัง event.waitUntil คือ ถูกปฏิเสธ ในกรณีนี้ ระบบจะทิ้ง Service Worker

หากสัญญาไว้ได้รับการแก้ไขแล้ว การติดตั้งสำเร็จและสถานะของโปรแกรมทำงานของบริการจะเปลี่ยนเป็น 'installed' จากนั้นจะเปิดใช้งาน

การดำเนินการ

หากการลงทะเบียนและติดตั้งสำเร็จ โปรแกรมทำงานของบริการจะเปิดใช้งาน และสถานะของโปรแกรมจะกลายเป็น 'activating' งานสามารถดำเนินการได้ในระหว่างการเปิดใช้งานในโปรแกรมทำงานของบริการ activate เหตุการณ์ งานทั่วไปในเหตุการณ์นี้ก็คือการพรรณาแคชเก่า แต่สำหรับโปรแกรมทำงานของบริการใหม่ ไม่เกี่ยวข้องกับตอนนี้ และจะขยายการใช้งานเมื่อเราพูดคุยเกี่ยวกับการอัปเดตโปรแกรมทำงานของบริการ

สำหรับโปรแกรมทำงานของบริการใหม่ activate จะเริ่มทำงานทันทีหลังจากที่ install สำเร็จ เมื่อการเปิดใช้งานเสร็จสิ้น สถานะของโปรแกรมทำงานของบริการจะกลายเป็น 'activated' ให้สังเกตว่าโดยค่าเริ่มต้น Service Worker ใหม่จะไม่เริ่มควบคุมหน้าจนกว่าจะถึงการนำทางครั้งถัดไปหรือการรีเฟรชหน้าเว็บ

การจัดการการอัปเดต Service Worker

เมื่อทำให้ Service Worker รายแรกใช้งานได้แล้ว เราน่าจะต้องมีการอัปเดตในภายหลัง ตัวอย่างเช่น อาจต้องมีการอัปเดตหากมีการเปลี่ยนแปลงในการจัดการคำขอหรือตรรกะการแคชล่วงหน้า

เมื่อมีการอัปเดต

เบราว์เซอร์จะตรวจหาการอัปเดตของ Service Worker ในกรณีต่อไปนี้

  • ผู้ใช้ไปยังหน้าเว็บภายในขอบเขตของ Service Worker
  • navigator.serviceWorker.register() จะได้รับการเรียกด้วย URL ที่แตกต่างจาก Service Worker ที่ติดตั้งไว้ในปัจจุบัน แต่อย่าเปลี่ยน URL ของ Service Worker
  • navigator.serviceWorker.register() ด้วย URL เดียวกันกับ Service Worker ที่ติดตั้งไว้ แต่ใช้ขอบเขตต่างกัน ขอย้ำอีกครั้งว่าให้หลีกเลี่ยงปัญหานี้โดยคงขอบเขตไว้ที่รูทของต้นทาง หากเป็นไปได้
  • เมื่อเกิดเหตุการณ์ต่างๆ เช่น 'push' หรือ 'sync' มีการทริกเกอร์ภายใน 24 ชั่วโมงที่ผ่านมา แต่ไม่ต้องกังวลกับเหตุการณ์เหล่านี้

การอัปเดตเกิดขึ้นได้อย่างไร

หากทราบว่าเวลาที่เบราว์เซอร์อัปเดตโปรแกรมทำงานของบริการมีความสำคัญ แต่ "วิธีการ" ก็คือ ในกรณีที่ URL หรือขอบเขตของโปรแกรมทำงานของบริการไม่มีการเปลี่ยนแปลง โปรแกรมทำงานของบริการที่ติดตั้งอยู่ในขณะนี้จะอัปเดตเป็นเวอร์ชันใหม่เท่านั้นเมื่อมีการเปลี่ยนแปลงเนื้อหา

โดยเบราว์เซอร์จะตรวจหาการเปลี่ยนแปลงด้วยวิธีต่อไปนี้

  • การเปลี่ยนแปลงแบบไบต์ต่อไบต์ในสคริปต์ที่ขอโดย importScripts หากมี
  • การเปลี่ยนแปลงโค้ดระดับบนสุดของโปรแกรมทำงานของบริการ ซึ่งส่งผลต่อลายนิ้วมือที่เบราว์เซอร์สร้างขึ้น

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

เรียกใช้การตรวจสอบการอัปเดตด้วยตนเอง

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

navigator.serviceWorker.ready.then((registration) => {
  registration.update();
});

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

การติดตั้ง

เมื่อใช้ Bundler สร้างเนื้อหาแบบคงที่ เนื้อหาเหล่านั้นจะมีแฮชในชื่อ เช่น framework.3defa9d2.js สมมติว่าเนื้อหาบางส่วนได้รับการแคชล่วงหน้าสำหรับการเข้าถึงแบบออฟไลน์ในภายหลัง การดำเนินการนี้จะต้องมีการอัปเดต Service Worker เพื่อแคชเนื้อหาที่อัปเดตไว้ล่วงหน้า

self.addEventListener('install', (event) => {
  const cacheKey = 'MyFancyCacheName_v2';

  event.waitUntil(caches.open(cacheKey).then((cache) => {
    // Add all the assets in the array to the 'MyFancyCacheName_v2'
    // `Cache` instance for later use.
    return cache.addAll([
      '/css/global.ced4aef2.css',
      '/css/home.cbe409ad.css',
      '/js/home.109defa4.js',
      '/js/jquery.38caf32d.js'
    ]);
  }));
});

มี 2 สิ่งที่แตกต่างจากตัวอย่างเหตุการณ์ install แรกจากก่อนหน้านี้

  1. สร้างอินสแตนซ์ Cache ใหม่ที่มีคีย์ 'MyFancyCacheName_v2' แล้ว
  2. มีการเปลี่ยนแปลงชื่อเนื้อหาที่แคชล่วงหน้า

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

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

การดำเนินการ

เมื่อติดตั้ง Service Worker ที่อัปเดตแล้วและระยะรอสิ้นสุด โปรแกรมจะทำงาน และระบบจะทิ้ง Service Worker เดิม งานทั่วไปที่ต้องทำในเหตุการณ์ activate ของ Service Worker ที่อัปเดตคือการลบแคชเก่า นำแคชเก่าออกโดยรับคีย์สำหรับอินสแตนซ์ Cache ที่เปิดอยู่ทั้งหมด caches.keys และลบแคชที่ไม่อยู่ในรายการอนุญาตที่กำหนด caches.delete:

self.addEventListener('activate', (event) => {
  // Specify allowed cache keys
  const cacheAllowList = ['MyFancyCacheName_v2'];

  // Get all the currently active `Cache` instances.
  event.waitUntil(caches.keys().then((keys) => {
    // Delete all caches that aren't in the allow list:
    return Promise.all(keys.map((key) => {
      if (!cacheAllowList.includes(key)) {
        return caches.delete(key);
      }
    }));
  }));
});

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

เหตุการณ์ activate จะเสร็จสิ้นหลังจากนำแคชเก่าออกแล้ว ในจุดนี้ Service Worker ใหม่จะเข้าควบคุมหน้านี้ แทนที่อันเก่าในที่สุด

วงจรชีวิตต้องดำเนินต่อไป

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

สำหรับผู้ที่สนใจเจาะลึกในหัวข้อนี้ คุณควรไปลอง บทความนี้โดย Jake Archibald มีข้อจำกัดเล็กๆ น้อยๆ มากมาย เกี่ยวกับการเต้นตลอดวงจรของบริการ แต่เป็นสิ่งที่รู้ได้จริง และความรู้ดังกล่าวจะมีประสิทธิภาพมากขึ้นเมื่อใช้ Workbox