แนวคิด Service Worker

วิธีคิดเมื่อนึกถึงโปรแกรมทำงานของบริการ

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

แต่ Service Worker นั้นไม่เหมือนกับนักพัฒนาซอฟต์แวร์เว็บส่วนใหญ่ที่เราคุ้นเคย โดยมาพร้อมกับเส้นทางการเรียนรู้ที่สูงชันและอุปสรรคต่างๆ ที่คุณต้องระวัง

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

เหมือนกัน แต่แตกต่างกัน

ขณะเขียนโค้ด Service Worker หลายสิ่งหลายอย่างจะรู้สึกคุ้นเคย คุณจะได้ใช้ฟีเจอร์ภาษา JavaScript ใหม่ๆ ที่ชื่นชอบ คุณฟังเหตุการณ์ในวงจรเช่นเดียวกับเหตุการณ์ UI คุณสามารถจัดการขั้นตอนการควบคุมด้วยคำสัญญาอย่างที่คุณคุ้นเคย

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

เลเยอร์ใหม่

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

Service Worker ทำหน้าที่เป็นชั้นกลางระหว่างไคลเอ็นต์กับเซิร์ฟเวอร์

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

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

ในเกม Service Workies เราครอบคลุมรายละเอียดมากมายของวงจรการทำงานของโปรแกรมทำงานของบริการ และให้คุณได้ฝึกทำงานกับโปรแกรมทำงานของบริการได้มากมาย

มีประสิทธิภาพ แต่มีข้อจำกัด

การมี Service Worker บนเว็บไซต์ของคุณจะให้ประโยชน์ที่น่าทึ่ง เว็บไซต์ของคุณสามารถทำสิ่งต่อไปนี้

  • ทำงานได้อย่างราบรื่นแม้ว่าผู้ใช้ออฟไลน์อยู่
  • ได้รับการปรับปรุงประสิทธิภาพครั้งใหญ่ผ่านการแคช
  • ใช้ข้อความ Push
  • ติดตั้งเป็น PWA

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

  • localStorage
  • DOM
  • หน้าต่าง

ข่าวดีคือมีวิธีสื่อสารกับ Service Worker ได้หลายวิธี ไม่ว่าจะเป็น postMessage โดยตรง, ช่องทางส่งข้อความแบบหนึ่งต่อหนึ่ง และช่องออกอากาศ 1 ต่อหลายรายการ

อายุยาวนาน แต่อายุสั้น

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

ใน 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"]);
    })
  );
});

เฝ้าระวังสถานการณ์โลก

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

ลองดูตัวอย่างนี้ที่ใช้สถานะทั่วโลก

const favoriteNumber = Math.random();
let hasHandledARequest = false;

self.addEventListener("fetch", event => {
  console.log(favoriteNumber);
  console.log(hasHandledARequest);
  hasHandledARequest = true;
});

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

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

ใน Service Workies บทที่ 3 เราแสดงภาพของ Service Worker ที่หยุดลงเป็นสีทั้งหมดในขณะที่รอให้เครื่องทำงาน

การแสดงภาพ Service Worker ที่หยุดทำงาน

รวมกัน แต่แยกจากกัน

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

การรับส่งข้อความกับแคชของโปรแกรมทำงานของบริการรายอื่น

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

  • SW2 อาจลบแคชที่ SW1 ใช้อยู่
  • SW2 อาจแก้ไขเนื้อหาของแคชที่ SW1 ใช้อยู่ ซึ่งทำให้ SW1 ตอบสนองด้วยเนื้อหาที่หน้าเว็บที่ไม่คาดคิด

ข้าม ข้าม รอ

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

เริ่มล้าง

วิธีป้องกันไม่ให้โปรแกรมทำงานของบริการ (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 ทำงานถึงสถานะ activated คุณจะทราบว่าได้เข้ามาแทนที่แล้ว และ Service Worker เดิมจะทำงานซ้ำซ้อน ณ จุดนี้ เป็นเรื่องสำคัญที่จะต้องล้างข้อมูลหลังจาก Service Worker เก่า วิธีนี้ไม่เพียงเคารพผู้ใช้ ขีดจำกัดพื้นที่เก็บข้อมูลแคช แต่ก็สามารถป้องกันข้อบกพร่องที่ไม่ได้ตั้งใจได้ด้วย

เมธอด caches.match() เป็นแป้นพิมพ์ลัดที่มักใช้ในการเรียกข้อมูลรายการจากแคชใดก็ได้ที่มีข้อมูลตรงกัน แต่จะทำซ้ำผ่านแคชตามลำดับที่สร้างขึ้น สมมติว่าคุณมีไฟล์สคริปต์ 2 เวอร์ชัน app.js ในแคช 2 รายการ คือ assets-1 และ assets-2 หน้าเว็บของคุณต้องการสคริปต์ที่ใหม่กว่าซึ่งจัดเก็บไว้ใน assets-2 แต่หากคุณยังไม่ได้ลบแคชเก่า caches.match('app.js') จะคืนค่าเวอร์ชันเก่าจาก assets-1 และอาจทำให้เว็บไซต์เสียหาย

เพียงแค่ลบแคชที่ 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 ที่คุณจะได้เรียนรู้วิธีทำงานของบริการทำงานด้านสัตว์เพื่อฆ่าสัตว์ที่ออฟไลน์