วิธีคิดเมื่อนึกถึงโปรแกรมทำงานของบริการ
Service Worker มีประสิทธิภาพและคุ้มค่ากับการเรียนรู้ ซึ่งช่วยให้คุณมอบประสบการณ์ระดับใหม่ให้แก่ผู้ใช้ เว็บไซต์โหลดได้ทันที ซึ่งสามารถทำงานได้แบบออฟไลน์ ติดตั้งเป็นแอปที่ใช้สำหรับแพลตฟอร์มโดยเฉพาะ จนดูสวยงามเหมือนอยู่ในแพลตฟอร์ม แต่เข้าถึงและใช้งานเว็บได้อย่างอิสระ
แต่โปรแกรมทำงานของบริการนั้นไม่เหมือนกับนักพัฒนาเว็บส่วนใหญ่ที่เราคุ้นเคย การเรียนรู้มีความท้าทายสูงและอุปสรรคต่างๆ ที่คุณควรระวัง
Google Developers และฉันเพิ่งทำงานร่วมกันในโครงการ Service Workies ซึ่งเป็นเกมฟรีที่ช่วยให้เข้าใจโปรแกรมทำงานของบริการได้ ในระหว่างที่สร้างและทำงานกับ Service Worker ที่ซับซ้อน ก็เจอปัญหาอยู่บ้าง สิ่งที่ช่วยได้มากที่สุดคือการคิดคำอุปมาอุปไมยที่เป็นรูปธรรมจำนวนหนึ่ง ในโพสต์นี้ เราจะมาสำรวจแบบจำลองทางความคิดเหล่านี้และหลอมรวมสมองของเราเกี่ยวกับลักษณะเฉพาะที่ขัดแย้งกันซึ่งทำให้โปรแกรมทำงานของบริการนั้นทั้งซับซ้อนและน่าอัศจรรย์
เหมือนกันแต่ต่างกัน
มีหลายอย่างที่คุณคุ้นเคยขณะเขียนโค้ด Service Worker คุณจะได้ใช้ฟีเจอร์ภาษา JavaScript ใหม่ที่ชื่นชอบ คุณจะฟังเหตุการณ์ในวงจรได้เช่นเดียวกับเหตุการณ์ UI คุณจะจัดการขั้นตอนการควบคุมด้วยคำสัญญาได้เหมือนที่เคย
แต่ลักษณะการทำงานอื่นๆ ของโปรแกรมทำงานของบริการทำให้คุณรู้สึกสับสน โดยเฉพาะอย่างยิ่งเมื่อคุณรีเฟรชหน้าเว็บและไม่เห็นการเปลี่ยนแปลงโค้ดของคุณมีผล
เลเยอร์ใหม่
โดยปกติแล้ว เมื่อสร้างเว็บไซต์ คุณจะมีเพียง 2 เลเยอร์ที่ต้องคำนึงถึง ได้แก่ ไคลเอ็นต์และเซิร์ฟเวอร์ Service Worker เป็นเลเยอร์ใหม่ล่าสุดที่อยู่ตรงกลาง
ให้คิดว่าโปรแกรมทำงานของบริการเป็นส่วนขยายของเบราว์เซอร์ประเภทหนึ่งที่เว็บไซต์ของคุณติดตั้งในเบราว์เซอร์ของผู้ใช้ได้ เมื่อติดตั้งแล้ว โปรแกรมทำงานของบริการจะextendsเบราว์เซอร์ของเว็บไซต์ด้วยเลเยอร์กลางที่มีประสิทธิภาพ เลเยอร์ Service Worker นี้สามารถสกัดกั้นและจัดการคำขอทั้งหมดที่เว็บไซต์ของคุณสร้างได้
เลเยอร์ Service Worker มีวงจรการใช้งานของตัวเองซึ่งแยกต่างหากจากแท็บเบราว์เซอร์ การรีเฟรชหน้าแบบง่ายๆ ไม่เพียงพอที่จะอัปเดต Service Worker แบบที่ตัวคุณเองไม่คิดว่าจะมีการรีเฟรชหน้าเพื่ออัปเดตโค้ดที่ทำให้ใช้งานได้ในเซิร์ฟเวอร์ แต่ละเลเยอร์มีกฎการอัปเดตของตัวเองโดยเฉพาะ
ในเกม Service Workies เราจะพูดถึงรายละเอียดมากมายเกี่ยวกับวงจรการทำงานของ Service Worker และให้คุณฝึกฝนการใช้งานได้มากมาย
มีประสิทธิภาพแต่มีจำกัด
การมี Service Worker ไว้บนเว็บไซต์ทำให้คุณได้รับประโยชน์อย่างน่าทึ่ง เว็บไซต์ของคุณสามารถ:
- ทำงานได้อย่างไร้ที่ติแม้ในขณะที่ผู้ใช้ออฟไลน์
- ได้รับการปรับปรุงประสิทธิภาพอย่างมากจากการใช้การแคช
- ใช้ข้อความ Push
- ติดตั้งเป็น PWA
แต่โปรแกรมทำงานของบริการก็มีข้อจำกัดเรื่องการออกแบบมากเช่นกัน โดยไม่สามารถดำเนินการบางอย่างพร้อมกันหรือในชุดข้อความเดียวกันกับเว็บไซต์ ซึ่งหมายความว่าคุณจะเข้าถึงรายการต่อไปนี้ไม่ได้
- localStorage
- DOM
- หน้าต่าง
ข่าวดีคือมีหลากหลายวิธีที่หน้าเว็บของคุณสามารถสื่อสารกับโปรแกรมทำงานของบริการ ซึ่งรวมถึง postMessage
โดยตรง ช่องข้อความแบบหนึ่งต่อหนึ่ง และช่องออกอากาศแบบหนึ่งต่อหลาย
มีอายุสั้น แต่มีอายุสั้น
โปรแกรมทำงานของบริการที่ใช้งานอยู่จะยังทำงานต่อไป แม้ว่าผู้ใช้จะออกจากเว็บไซต์ของคุณหรือปิดแท็บไปแล้วก็ตาม โดยเบราว์เซอร์จะเก็บ Service Worker นี้ไว้เพื่อให้พร้อมใช้งานเมื่อผู้ใช้กลับมาที่เว็บไซต์ของคุณในครั้งถัดไป ก่อนส่งคำขอแรก โปรแกรมทำงานของบริการจะมีโอกาสขัดขวางคำสั่งดังกล่าวและเข้าควบคุมหน้าเว็บ ซึ่งเป็นสิ่งที่ทำให้เว็บไซต์ทำงานแบบออฟไลน์ได้ โปรแกรมทำงานของบริการจะแสดงหน้าเว็บในเวอร์ชันแคชเอง แม้ว่าผู้ใช้จะไม่เชื่อมต่อกับอินเทอร์เน็ตก็ตาม
ใน Service Workies เราแสดงภาพของแนวคิดนี้กับ Kolohe (ซึ่งเป็นโปรแกรมทำงานของบริการที่เป็นมิตร) ที่สกัดกั้นและจัดการคำขอ
หยุดทำงานแล้ว
แม้ว่าโปรแกรมทำงานของบริการจะดูเหมือนเป็นอมตะ แต่โปรแกรมดังกล่าวสามารถหยุดการทำงานได้ทุกเมื่อ เบราว์เซอร์ไม่ต้องการสิ้นเปลืองทรัพยากรไปกับโปรแกรมทำงานของบริการที่ไม่ได้ดำเนินการใดๆ อยู่ การหยุดทํางานไม่ใช่สิ่งเดียวกับการถูกยกเลิก ทั้งนี้ Service Worker จะยังคงติดตั้งและเปิดใช้งานอยู่ ก็แค่ทำให้นอนหลับ ในครั้งต่อไปที่จำเป็นต้องใช้ (เช่น ในการจัดการคำขอ) เบราว์เซอร์จะปลุกระบบให้กลับมาทำงาน
waitUntil
เนื่องจากเป็นไปได้ที่จะเข้าสู่โหมดสลีปได้อยู่เสมอ โปรแกรมทำงานของบริการจึงต้องการวิธีที่จะทำให้เบราว์เซอร์รู้ว่าเบราว์เซอร์กำลังทำอะไรที่สำคัญและไม่รู้สึกอยากงีบหลับ event.waitUntil()
จะเข้ามามีบทบาทในส่วนนี้ เมธอดนี้จะขยายอายุการใช้งานของเมธอดให้มีการใช้งาน เพื่อไม่ให้มีการหยุดการทำงานและจากไปยังระยะถัดไปของวงจรจนกว่าเราจะพร้อมทำงาน ซึ่งจะทำให้เรามีเวลาในการตั้งค่าแคช ดึงทรัพยากรจากเครือข่าย เป็นต้น
ตัวอย่างนี้บอกเบราว์เซอร์ว่า Service Worker จะยังติดตั้งไม่เสร็จจนกว่าจะมีการสร้างแคช assets
และใส่ภาพดาบขึ้นมา
self.addEventListener("install", event => {
event.waitUntil(
caches.open("assets").then(cache => {
return cache.addAll(["/weapons/sword/blade.png"]);
})
);
});
เฝ้าระวังสถานการณ์ทั่วโลก
เมื่อเริ่ม/หยุด ระบบจะรีเซ็ตขอบเขตรวมของ Service Worker ดังนั้น โปรดระวังอย่าใช้สถานะระดับโลกใดๆ ใน Service Worker ไม่เช่นนั้นคุณจะรู้สึกเสียใจในครั้งถัดไปที่กลับมาทำงานและมีสถานะต่างจากที่คาดไว้
ลองดูตัวอย่างนี้ที่ใช้รัฐทั่วโลก
const favoriteNumber = Math.random();
let hasHandledARequest = false;
self.addEventListener("fetch", event => {
console.log(favoriteNumber);
console.log(hasHandledARequest);
hasHandledARequest = true;
});
ในแต่ละคำขอ โปรแกรมทำงานของบริการนี้จะบันทึกหมายเลข สมมติว่า 0.13981866382421893
ตัวแปร hasHandledARequest
จะเปลี่ยนเป็น true
ด้วย ตอนนี้โปรแกรมทำงานของบริการจะไม่มีการใช้งานชั่วครู่ ดังนั้นเบราว์เซอร์จึงหยุดการทำงาน ครั้งถัดไปที่มีคำขอ จำเป็นต้องใช้ Service Worker อีกครั้ง เบราว์เซอร์จึงปลุกระบบให้ทำงาน ระบบจะประเมินอีกครั้งสคริปต์ ขณะนี้ hasHandledARequest
จะรีเซ็ตเป็น false
และ favoriteNumber
แตกต่างจาก 0.5907281835659033
อย่างสิ้นเชิง
คุณใช้สถานะที่เก็บไว้ในโปรแกรมทำงานของบริการไม่ได้ นอกจากนี้ การสร้างอินสแตนซ์ของสิ่งต่างๆ เช่น ช่องทางข้อความ อาจทำให้เกิดข้อบกพร่องได้ นั่นคือคุณจะได้รับอินสแตนซ์ใหม่ทุกครั้งที่โปรแกรมทำงานของบริการหยุด/เริ่มทำงาน
ใน Service Workies บทที่ 3 เราแสดงภาพโปรแกรมทำงานของบริการที่หยุดทำงานแล้วเป็นสีที่สูญเสียไปทั้งหมดขณะที่รอให้ระบบปลุกระบบ
เมื่ออยู่ด้วยกัน แต่แยกจากกัน
สามารถควบคุมหน้าเว็บได้โดย Service Worker ครั้งละ 1 ตัวเท่านั้น แต่สามารถติดตั้งโปรแกรมทำงานของบริการได้ 2 รายการพร้อมกัน เมื่อเปลี่ยนแปลงโค้ด Service Worker และรีเฟรชหน้านี้ แสดงว่าคุณไม่ได้แก้ไข Service Worker เลย โปรแกรมทำงานของบริการจะเปลี่ยนแปลงไม่ได้ เพราะจะเป็นการสร้างใหม่แทน Service Worker ใหม่นี้ (เรียกว่า SW2) จะติดตั้งแต่จะยังเปิดใช้งานไม่ได้ และจะต้องรอให้ Service Worker (SW1) ปัจจุบันสิ้นสุดการทำงาน (เมื่อผู้ใช้ออกจากเว็บไซต์ของคุณ)
กำลังยุ่งกับแคชของ Service Worker อื่น
ขณะติดตั้ง SW2 จะตั้งค่าสิ่งต่างๆ ได้ ซึ่งมักจะสร้างและป้อนข้อมูลแคช แต่โปรดทราบด้วยว่า Service Worker ใหม่นี้มีสิทธิ์เข้าถึงทุกอย่างที่ Service Worker ปัจจุบันมีสิทธิ์เข้าถึง หากคุณไม่ระวัง โปรแกรมทำงานของบริการที่รออยู่ใหม่อาจทำให้โปรแกรมทำงานของบริการปัจจุบันยุ่งเหยิงได้ ตัวอย่างบางส่วนที่อาจก่อให้เกิดปัญหา
- SW2 อาจลบแคชที่ SW1 ใช้งานอยู่
- SW2 สามารถแก้ไขเนื้อหาของแคชที่ SW1 ใช้อยู่ ทำให้ SW1 ตอบสนองด้วยเนื้อหาที่หน้าเว็บไม่คาดหวัง
ข้ามการข้ามขณะรอ
นอกจากนี้ โปรแกรมทำงานของบริการอาจใช้เมธอด skipWaiting()
ที่มีความเสี่ยงเพื่อควบคุมหน้าเว็บทันทีที่ติดตั้งเสร็จแล้ว ซึ่งโดยทั่วไปไม่ใช่ความคิดที่ดี เว้นแต่คุณจะตั้งใจเปลี่ยนโปรแกรมทำงานของบริการที่มีข้อบกพร่อง Service Worker ใหม่อาจใช้ทรัพยากรที่อัปเดตซึ่งหน้าปัจจุบันไม่ต้องการ ซึ่งทําให้เกิดข้อผิดพลาดและข้อบกพร่อง
เริ่มทำความสะอาด
วิธีป้องกันไม่ให้โปรแกรมทำงานของบริการทำงานซ้ำๆ คือการตรวจสอบว่าโปรแกรมทำงานนั้นใช้แคชที่แตกต่างกัน วิธีที่ง่ายที่สุดในการดำเนินการดังกล่าวคือการกำหนดเวอร์ชันของชื่อแคชที่พวกเขาใช้
const version = 1;
const assetCacheName = `assets-${version}`;
self.addEventListener("install", event => {
caches.open(assetCacheName).then(cache => {
// confidently do stuff with your very own cache
});
});
เมื่อทำให้ Service Worker ใหม่ใช้งานได้ คุณจะต้องใช้งาน version
เพื่อให้ทำงานได้ตามที่ต้องการโดยมีแคชแยกต่างหากจาก Service Worker ก่อนหน้า
ล้างปลายทาง
เมื่อ Service Worker ถึงสถานะของ activated
แล้ว คุณจะทราบได้ว่ามีการเข้าควบคุมการทำงาน และ Service Worker ก่อนหน้าซ้ำซ้อนอยู่ ณ จุดนี้ถือเป็นเรื่องสำคัญที่ต้องล้างข้อมูลหลังจากใช้โปรแกรมทำงานของบริการเก่า ซึ่งไม่เพียงแต่ทำตามขีดจำกัดพื้นที่เก็บข้อมูลแคชของผู้ใช้เท่านั้น แต่ยังป้องกันข้อบกพร่องโดยไม่ตั้งใจอีกด้วย
เมธอด caches.match()
เป็นทางลัดที่ใช้บ่อยสำหรับการเรียกข้อมูลรายการจากแคชใดๆ ที่มีรายการที่ตรงกัน แต่จะทำซ้ำผ่านแคชตามลำดับที่สร้างขึ้น สมมติว่าคุณมีไฟล์สคริปต์ app.js
2 เวอร์ชันในแคชที่ต่างกัน 2 แคช คือ assets-1
และ assets-2
หน้าเว็บของคุณต้องการสคริปต์ที่ใหม่กว่าที่จัดเก็บไว้ใน assets-2
แต่หากคุณไม่ได้ลบแคชเก่า caches.match('app.js')
จะส่งคืนแคชเก่าจาก assets-1
ซึ่งมีแนวโน้มสูงที่เว็บไซต์ของคุณจะทำให้เสีย
สิ่งที่ต้องล้างหลังจาก Service Worker ก่อนหน้าคือลบแคชที่ Service Worker ใหม่ไม่จำเป็น ดังนี้
const version = 2;
const assetCacheName = `assets-${version}`;
self.addEventListener("activate", event => {
event.waitUntil(
caches.keys().then(cacheNames => {
return Promise.all(
cacheNames.map(cacheName => {
if (cacheName !== assetCacheName){
return caches.delete(cacheName);
}
});
);
});
);
});
การป้องกันไม่ให้โปรแกรมทำงานของบริการทำงานซ้ำๆ ต้องอาศัยการทำงานและวินัยเพียงเล็กน้อย แต่ก็คุ้มค่ากับปัญหา
แนวคิด Service Worker
การมีแนวคิดที่ถูกต้องและคำนึงถึงโปรแกรมทำงานของบริการจะช่วยให้คุณสร้างแนวคิดของคุณเองได้อย่างมั่นใจ เมื่อเข้าใจแล้ว คุณจะสร้างประสบการณ์ที่ยอดเยี่ยมสำหรับผู้ใช้ได้
หากคุณต้องการทำความเข้าใจทั้งหมดนี้ด้วยการเล่นเกม ก็โชคดีแล้ว! ไปเล่น Service Workies เพื่อให้คุณได้เรียนรู้วิธีของ Service Worker ในการปราบสัตว์ออฟไลน์