การเขียนสคริปต์ Service Worker

Codelab นี้เป็นส่วนหนึ่งของหลักสูตรการฝึกอบรมการพัฒนา Progressive Web App ซึ่งพัฒนาโดยทีมฝึกอบรมของ Google Developers คุณจะได้รับประโยชน์สูงสุดจากหลักสูตรนี้หากทำตาม Codelab ตามลำดับ

ดูรายละเอียดทั้งหมดเกี่ยวกับหลักสูตรได้ที่ภาพรวมการพัฒนา Progressive Web App

บทนำ

ในห้องทดลองนี้ เราจะแนะนำขั้นตอนการสร้าง Service Worker อย่างง่ายและอธิบายวงจรของ Service Worker

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

  • สร้างสคริปต์ Service Worker พื้นฐาน ติดตั้ง และทำการแก้ไขข้อบกพร่องอย่างง่าย

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

  • JavaScript และ HTML พื้นฐาน
  • แนวคิดและไวยากรณ์พื้นฐานของ Promise ใน ES2015
  • วิธีเปิดใช้ Developer Console

สิ่งที่ต้องมีก่อนเริ่มต้น

ดาวน์โหลดหรือโคลนที่เก็บ pwa-training-labs จาก GitHub และติดตั้ง Node.js เวอร์ชัน LTS หากจำเป็น

ไปที่ไดเรกทอรี service-worker-lab/app/ แล้วเริ่มเซิร์ฟเวอร์การพัฒนาซอฟต์แวร์ภายใน

cd service-worker-lab/app
npm install
node server.js

คุณสิ้นสุดการทำงานของเซิร์ฟเวอร์ได้ทุกเมื่อด้วย Ctrl-c

เปิดเบราว์เซอร์ แล้วไปที่ localhost:8081/

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

เปิดโฟลเดอร์ service-worker-lab/app/ ในโปรแกรมแก้ไขข้อความที่ต้องการ app/ โฟลเดอร์คือที่ที่คุณจะสร้าง Lab

โฟลเดอร์นี้ประกอบด้วย

  • below/another.html, js/another.js, js/other.js และ other.html เป็นทรัพยากรตัวอย่างที่เราใช้เพื่อทดสอบขอบเขตของ Service Worker
  • โฟลเดอร์ styles/ มีสไตล์ชีตแบบเรียงซ้อนสำหรับห้องทดลองนี้
  • test/ โฟลเดอร์มีไฟล์สำหรับการทดสอบความคืบหน้า
  • index.html คือหน้า HTML หลักสำหรับเว็บไซต์/แอปพลิเคชันตัวอย่าง
  • service-worker.js คือไฟล์ JavaScript ที่ใช้สร้าง Service Worker
  • package.json และ package-lock.json จะติดตามแพ็กเกจโหนดที่ใช้ในโปรเจ็กต์นี้
  • server.js เป็นเซิร์ฟเวอร์ Express อย่างง่ายที่เราใช้เพื่อโฮสต์แอป

เปิด service-worker.js ในโปรแกรมแก้ไขข้อความ โปรดทราบว่าไฟล์ว่างเปล่า เรายังไม่ได้เพิ่มโค้ดใดๆ เพื่อเรียกใช้ภายใน Service Worker

เปิด index.html ในโปรแกรมแก้ไขข้อความ

ภายในแท็ก <script> ให้เพิ่มโค้ดต่อไปนี้เพื่อลงทะเบียน Service Worker

if ('serviceWorker' in navigator) {
  window.addEventListener('load', () => {
    navigator.serviceWorker.register('service-worker.js')
    .then(registration => {
      console.log('Service Worker is registered', registration);
    })
    .catch(err => {
      console.error('Registration failed:', err);
    });
  });
}

บันทึกสคริปต์แล้วรีเฟรชหน้าเว็บ คอนโซลควรแสดงข้อความที่ระบุว่ามีการลงทะเบียน Service Worker แล้ว ใน Chrome คุณสามารถตรวจสอบว่ามีการลงทะเบียน Service Worker หรือไม่โดยเปิดเครื่องมือสำหรับนักพัฒนาซอฟต์แวร์ (Control + Shift + I ใน Windows และ Linux หรือ ⌘ + alt + I ใน Mac) คลิกแท็บแอปพลิเคชัน แล้วคลิกตัวเลือก Service Worker คุณควรเห็นข้อมูลที่มีลักษณะคล้ายด้านล่าง

ไม่บังคับ: เปิดเว็บไซต์ในเบราว์เซอร์ที่ไม่รองรับและตรวจสอบว่าเงื่อนไขการตรวจสอบการรองรับทำงาน

คำอธิบาย

โค้ดด้านบนจะลงทะเบียนไฟล์ service-worker.js เป็น Service Worker โดยจะตรวจสอบก่อนว่าเบราว์เซอร์รองรับ Service Worker หรือไม่ คุณควรทำเช่นนี้ทุกครั้งที่ลงทะเบียน Service Worker เนื่องจากเบราว์เซอร์บางตัวอาจไม่รองรับ Service Worker จากนั้นโค้ดจะลงทะเบียน Service Worker โดยใช้เมธอด register ของ ServiceWorkerContainer API ซึ่งอยู่ในอินเทอร์เฟซ Navigator ของหน้าต่าง

navigator.serviceWorker.register(...) จะแสดงผล Promise ที่จะเปลี่ยนเป็นออบเจ็กต์ registration เมื่อลงทะเบียน Service Worker สำเร็จ หากการลงทะเบียนไม่สำเร็จ Promise จะปฏิเสธ

การเปลี่ยนแปลงสถานะของ Service Worker จะทริกเกอร์เหตุการณ์ใน Service Worker

เพิ่ม Listener เหตุการณ์

เปิด service-worker.js ในโปรแกรมแก้ไขข้อความ

เพิ่ม Listener เหตุการณ์ต่อไปนี้ลงใน Service Worker

self.addEventListener('install', event => {
  console.log('Service worker installing...');
  // Add a call to skipWaiting here
});

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

บันทึกไฟล์

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

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

คำอธิบาย

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

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

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

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

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

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

อัปเดต Service Worker

เพิ่มความคิดเห็นต่อไปนี้ที่ใดก็ได้ใน service-worker.js

// I'm a new service worker

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

ปิดหน้าเว็บทั้งหมดที่เชื่อมโยงกับ Service Worker จากนั้นให้เปิด localhost:8081/ อีกครั้ง บันทึกคอนโซลควรระบุว่าตอนนี้เปิดใช้งาน Service Worker ใหม่แล้ว

หมายเหตุ: หากได้รับผลลัพธ์ที่ไม่คาดคิด โปรดตรวจสอบว่าได้ปิดใช้แคช HTTP ในเครื่องมือสำหรับนักพัฒนาซอฟต์แวร์แล้ว

คำอธิบาย

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

ข้ามระยะการรอ

Service Worker ใหม่จะเปิดใช้งานได้ทันทีแม้ว่าจะมี Service Worker อยู่แล้วก็ตาม โดยการข้ามระยะการรอ

ใน service-worker.js ให้เพิ่มการเรียกใช้ skipWaiting ใน Listener เหตุการณ์ install ดังนี้

self.skipWaiting();

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

คำอธิบาย

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

สำหรับข้อมูลเพิ่มเติม

Service Worker สามารถทำหน้าที่เป็นพร็อกซีระหว่างเว็บแอปกับเครือข่ายได้

มาเพิ่มเครื่องมือฟังการดึงข้อมูลเพื่อสกัดกั้นคำขอจากโดเมนของเรากัน

เพิ่มโค้ดต่อไปนี้ไปยัง service-worker.js

self.addEventListener('fetch', event => {
  console.log('Fetching:', event.request.url);
});

บันทึกสคริปต์และรีเฟรชหน้าเว็บเพื่อติดตั้งและเปิดใช้งาน Service Worker ที่อัปเดต

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

คลิกลิงก์ไปยังหน้าอื่น อีกหน้า และกลับ

คุณจะเห็นเหตุการณ์การดึงข้อมูลในคอนโซลสำหรับแต่ละหน้าและเนื้อหาของหน้า บันทึกทั้งหมดสมเหตุสมผลไหม

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

คำอธิบาย

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

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

สำหรับข้อมูลเพิ่มเติม

รหัสโซลูชัน

หากต้องการรับสำเนารหัสที่ใช้งานได้ ให้ไปที่โฟลเดอร์ 04-intercepting-network-requests/

Service Worker มีขอบเขต ขอบเขตของ Service Worker จะกำหนดเส้นทางที่ Service Worker สกัดกั้นคำขอ

ค้นหาขอบเขต

อัปเดตรหัสการลงทะเบียนใน index.html โดยใช้ข้อมูลต่อไปนี้

if ('serviceWorker' in navigator) {
  window.addEventListener('load', () => {
    navigator.serviceWorker.register('service-worker.js')
    .then(registration => {
      console.log('SW registered with scope:', registration.scope);
    })
    .catch(err => {
      console.error('Registration failed:', err);
    });
  });
}

รีเฟรชเบราว์เซอร์ โปรดสังเกตว่าคอนโซลแสดงขอบเขตของ Service Worker (ในกรณีนี้คือ http://localhost:8081/)

คำอธิบาย

Promise ที่ register() แสดงผลจะเปลี่ยนเป็นออบเจ็กต์การลงทะเบียน ซึ่งมีขอบเขตของ Service Worker

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

ย้าย Service Worker

ย้าย service-worker.js ไปยังไดเรกทอรี below/ และอัปเดต URL ของ Service Worker ในโค้ดการลงทะเบียนใน index.html

ยกเลิกการลงทะเบียน Service Worker ปัจจุบันในเบราว์เซอร์ แล้วรีเฟรชหน้าเว็บ

คอนโซลแสดงว่าขอบเขตของ Service Worker เป็น http://localhost:8081/below/ แล้ว ใน Chrome คุณยังดูขอบเขตของ Service Worker ได้ในแท็บแอปพลิเคชันของ DevTools โดยทำดังนี้

กลับไปที่หน้าหลัก แล้วคลิกหน้าอื่น อีกหน้า และกลับ ระบบจะบันทึกคำขอเรียกข้อมูลใดบ้าง และมีอะไรบ้างที่ไม่ได้รวมอยู่ในนั้น

คำอธิบาย

ขอบเขตเริ่มต้นของ Service Worker คือเส้นทางไปยังไฟล์ Service Worker เนื่องจากตอนนี้ไฟล์ Service Worker อยู่ใน below/ ขอบเขตของไฟล์จึงเป็น below/ ตอนนี้คอนโซลจะบันทึกเหตุการณ์การดึงข้อมูลสำหรับ another.html, another.css และ another.js เท่านั้น เนื่องจากเป็นทรัพยากรเดียวที่อยู่ในขอบเขตของ Service Worker

ตั้งค่าขอบเขตที่กำหนดเอง

ย้าย Service Worker กลับไปที่ไดเรกทอรีรากของโปรเจ็กต์ (app/) และอัปเดต URL ของ Service Worker ในโค้ดการลงทะเบียนใน index.html

ใช้ข้อมูลอ้างอิงใน MDN เพื่อตั้งค่าขอบเขตของ Service Worker เป็นไดเรกทอรี below/ โดยใช้พารามิเตอร์ที่ไม่บังคับใน register()

ยกเลิกการลงทะเบียน Service Worker แล้วรีเฟรชหน้าเว็บ คลิกหน้าอื่น อีกหน้า และกลับ

อีกครั้งที่คอนโซลแสดงว่าขอบเขตของ Service Worker ตอนนี้คือ http://localhost:8081/below/ และบันทึกเหตุการณ์การดึงข้อมูลสำหรับ another.html, another.css และ another.js เท่านั้น

คำอธิบาย

คุณตั้งค่าขอบเขตที่กำหนดเองได้โดยส่งพารามิเตอร์เพิ่มเติมเมื่อลงทะเบียน เช่น

navigator.serviceWorker.register('/service-worker.js', {
  scope: '/kitten/'
});

ในตัวอย่างด้านบน ขอบเขตของ Service Worker จะตั้งค่าเป็น /kitten/ Service Worker จะสกัดกั้นคำขอจากหน้าเว็บใน /kitten/ และ /kitten/lower/ แต่จะไม่สกัดกั้นคำขอจากหน้าเว็บ เช่น /kitten หรือ /

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

สำหรับข้อมูลเพิ่มเติม

รหัสโซลูชัน

หากต้องการรับสำเนารหัสที่ใช้งานได้ ให้ไปที่โฟลเดอร์ solution/

ตอนนี้คุณมี Service Worker ที่ใช้งานได้แล้ว และเข้าใจวงจรของ Service Worker

สำหรับข้อมูลเพิ่มเติม

วงจรการทำงานของ Service Worker

หากต้องการดู Codelab ทั้งหมดในหลักสูตรการฝึกอบรม PWA โปรดดู Codelab ต้อนรับสำหรับหลักสูตร