คำแนะนำในการแคชที่จำเป็น

Andrew Guan
Andrew Guan

เว็บไซต์บางแห่งอาจต้องสื่อสารกับ Service Worker โดยไม่ต้องรับข้อมูลเกี่ยวกับผลลัพธ์ ตัวอย่างเช่น

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

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

แผนภาพของหน้าเว็บที่ขอทรัพยากรเพื่อแคชไปยัง Service Worker

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

เคสเวอร์ชันที่ใช้งานจริง

1-800-Flowers.com ใช้แคชแบบบังคับ (การเรียกข้อมูลล่วงหน้า) กับ Service Worker ผ่าน postMessage() เพื่อเรียกข้อมูลล่วงหน้าสำหรับสินค้ายอดนิยมในหน้าหมวดหมู่เพื่อเร่งความเร็วในการไปยังหน้ารายละเอียดผลิตภัณฑ์ในภายหลัง

โลโก้ของ 1-800 Flowers

โดยจะใช้แนวทางแบบผสมในการเลือกรายการที่จะโหลดล่วงหน้า ดังนี้

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

โดยจะใช้ Cache API เพื่อจัดเก็บคําตอบ JSON ดังนี้

โลโก้ของ 1-800 Flowers
การเรียกข้อมูลผลิตภัณฑ์ JSON ล่วงหน้าจากหน้าข้อมูลผลิตภัณฑ์ใน 1-800Flowers.com

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

การใช้ Workbox

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

หากต้องการสื่อสารหน้าเว็บกับ Service Worker ก่อนอื่นให้รับการอ้างอิงออบเจ็กต์ Workbox ไปยัง Service Worker ที่ลงทะเบียนไว้ ดังนี้

const wb = new Workbox('/sw.js');
wb.register();

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

wb.messageSW({"type": "PREFETCH", "payload": {"urls": ["/data1.json", "data2.json"]}}); });

ผู้ให้บริการจะใช้ตัวแฮนเดิล message เพื่อรับฟังข้อความเหล่านี้ ตัวเลือกนี้จะแสดงผลลัพธ์หรือไม่ก็ได้ แต่ในกรณีต่อไปนี้ก็ไม่จําเป็น

self.addEventListener('message', (event) => {
  if (event.data && event.data.type === 'PREFETCH') {
    // do something
  }
});

การใช้ API ของเบราว์เซอร์

หากไลบรารี Workbox ไม่เพียงพอต่อความต้องการ คุณสามารถใช้วิธีต่อไปนี้เพื่อติดตั้งใช้งานการสื่อสารระหว่างกรอบเวลากับบริการโดยใช้เบราว์เซอร์ API

postMessage API ใช้เพื่อสร้างกลไกการสื่อสารแบบทางเดียวจากหน้าเว็บไปยัง Service Worker ได้

หน้าเว็บเรียกใช้ postMessage() ในอินเทอร์เฟซ Service Worker ดังนี้

navigator.serviceWorker.controller.postMessage({
  type: 'MSG_ID',
  payload: 'some data to perform the task',
});

ผู้ให้บริการจะใช้ตัวแฮนเดิล message เพื่อรับฟังข้อความเหล่านี้

self.addEventListener('message', (event) => {
  if (event.data && event.data.type === MSG_ID) {
    // do something
  }
});

คุณไม่จำเป็นต้องใช้แอตทริบิวต์ {type : 'MSG_ID'} แต่นี่เป็นวิธีหนึ่งที่ช่วยให้หน้าเว็บส่งคำสั่งประเภทต่างๆ ไปยัง Service Worker ได้ (เช่น "to prefetch" เทียบกับ "to clear storage") เซอร์วิสเวิร์กสามารถแยกออกเป็นเส้นทางการดําเนินการที่แตกต่างกันตาม Flag นี้

หากการดำเนินการสำเร็จ ผู้ใช้จะได้รับสิทธิประโยชน์จากการดำเนินการดังกล่าว แต่หากไม่สำเร็จ การดำเนินการดังกล่าวจะไม่เปลี่ยนแปลงขั้นตอนหลักของผู้ใช้ ตัวอย่างเช่น เมื่อ 1-800-Flowers.com พยายามแคชล่วงหน้า หน้าเว็บไม่จำเป็นต้องทราบว่า Service Worker ทำงานสำเร็จหรือไม่ หากเป็นเช่นนั้น ผู้ใช้จะไปยังส่วนต่างๆ ได้เร็วขึ้น หากไม่ได้ผล หน้าเว็บจะยังคงต้องไปยังหน้าใหม่ โปรดรออีกสักครู่

ตัวอย่างการเรียกข้อมูลล่วงหน้าแบบง่าย

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

การใช้การโหลดล่วงหน้าในเว็บไซต์มีหลายวิธีดังนี้

สำหรับสถานการณ์การเรียกข้อมูลล่วงหน้าที่ค่อนข้างง่าย เช่น การเรียกข้อมูลล่วงหน้าของเอกสารหรือชิ้นงานเฉพาะ (JS, CSS ฯลฯ) เทคนิคเหล่านี้เป็นแนวทางที่ดีที่สุด

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

การมอบหมายการดำเนินการประเภทเหล่านี้ให้กับ Service Worker มีข้อดีดังนี้

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

โหลดหน้ารายละเอียดผลิตภัณฑ์ล่วงหน้า

ก่อนอื่น ให้ใช้ postMessage() ในอินเทอร์เฟซ Service Worker และส่งอาร์เรย์ของ URL ที่จะแคช

navigator.serviceWorker.controller.postMessage({
  type: 'PREFETCH',
  payload: {
    urls: [
      'www.exmaple.com/apis/data_1.json',
      'www.exmaple.com/apis/data_2.json',
    ],
  },
});

ใน Service Worker ให้ใช้ตัวแฮนเดิล message เพื่อดักรับและประมวลผลข้อความที่ส่งโดยแท็บที่ใช้งานอยู่

addEventListener('message', (event) => {
  let data = event.data;
  if (data && data.type === 'PREFETCH') {
    let urls = data.payload.urls;
    for (let i in urls) {
      fetchAsync(urls[i]);
    }
  }
});

ในโค้ดก่อนหน้า เราได้แนะนำฟังก์ชันตัวช่วยเล็กๆ ชื่อ fetchAsync() เพื่อวนซ้ำในอาร์เรย์ของ URL และส่งคำขอดึงข้อมูลสำหรับแต่ละรายการ

async function fetchAsync(url) {
  // await response of fetch call
  let prefetched = await fetch(url);
  // (optionally) cache resources in the service worker storage
}

เมื่อได้รับคำตอบแล้ว คุณจะใช้ส่วนหัวการแคชของทรัพยากรได้ อย่างไรก็ตาม ในหลายกรณี เช่น ในหน้ารายละเอียดผลิตภัณฑ์ ระบบจะไม่แคชทรัพยากร (ซึ่งหมายความว่ามีส่วนหัว Cache-control ของ no-cache) ในกรณีเช่นนี้ คุณสามารถลบล้างลักษณะการทำงานนี้ได้โดยจัดเก็บทรัพยากรที่ดึงข้อมูลไว้ในแคช Service Worker ซึ่งมีข้อดีเพิ่มเติมคืออนุญาตให้แสดงไฟล์ในสถานการณ์ออฟไลน์

นอกเหนือจากข้อมูล JSON

เมื่อดึงข้อมูล JSON จากปลายทางเซิร์ฟเวอร์แล้ว ข้อมูลดังกล่าวมักจะมี URL อื่นๆ ที่ควรมีการเรียกข้อมูลล่วงหน้าด้วย เช่น รูปภาพหรือข้อมูลปลายทางอื่นๆ ที่เชื่อมโยงกับข้อมูลระดับแรกนี้

สมมติว่าในตัวอย่างนี้ ข้อมูล JSON ที่แสดงผลคือข้อมูลของเว็บไซต์ช็อปปิ้งของใช้ทั่วไป

{
  "productName": "banana",
  "productPic": "https://cdn.example.com/product_images/banana.jpeg",
  "unitPrice": "1.99"
 }

แก้ไขโค้ด fetchAsync() ให้วนซ้ำรายการผลิตภัณฑ์และแคชรูปภาพหลักสำหรับผลิตภัณฑ์แต่ละรายการ

async function fetchAsync(url, postProcess) {
  // await response of fetch call
  let prefetched = await fetch(url);

  //(optionally) cache resource in the service worker cache

  // carry out the post fetch process if supplied
  if (postProcess) {
    await postProcess(prefetched);
  }
}

async function postProcess(prefetched) {
  let productJson = await prefetched.json();
  if (productJson && productJson.product_pic) {
    fetchAsync(productJson.product_pic);
  }
}

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

บทสรุป

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

ดูรูปแบบการสื่อสารเพิ่มเติมระหว่างหน้าเว็บและ Service Worker ได้ที่

  • การออกอากาศการอัปเดต: การเรียกหน้าเว็บจาก Service Worker เพื่อแจ้งเกี่ยวกับการอัปเดตที่สําคัญ (เช่น เว็บแอปเวอร์ชันใหม่พร้อมใช้งานแล้ว)
  • การสื่อสารแบบ 2 ทาง: การมอบหมายงานให้กับ Service Worker (เช่น การดาวน์โหลดขนาดใหญ่) และแจ้งข้อมูลความคืบหน้าให้หน้าเว็บทราบอยู่เสมอ