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

Andrew Guan
Andrew Guan

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

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

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

แผนภาพของหน้าที่มีคำขอทรัพยากรเพื่อแคชไปยังโปรแกรมทำงานของบริการ

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

กรณีการผลิต

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

โลโก้ 1-800 Flowers

โดยใช้แนวทางแบบผสมผสานในการตัดสินใจว่าจะดึงข้อมูลใดล่วงหน้า

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

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

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

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

การใช้ Workbox

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

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

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

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

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

Service Worker จะใช้ตัวแฮนเดิล 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',
});

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

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

คุณไม่จำเป็นต้องระบุแอตทริบิวต์ {type : 'MSG_ID'} โดยตรง แต่เป็นวิธีหนึ่งที่ช่วยให้หน้าเว็บส่งวิธีการประเภทต่างๆ ไปยัง Service Worker ได้ (ซึ่งก็คือ "การดึงข้อมูลล่วงหน้า" กับ "เพื่อล้างพื้นที่เก็บข้อมูล") Service Worker สามารถแตกข้อมูลไปยังเส้นทางการดำเนินการต่างๆ ตามแฟล็กนี้

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

ตัวอย่างการดึงข้อมูลล่วงหน้าแบบง่ายๆ

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

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

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

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

การมอบสิทธิ์การดำเนินการประเภทเหล่านี้ให้กับโปรแกรมทำงานของบริการมีข้อดีดังต่อไปนี้

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

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

ให้ใช้ 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',
    ],
  },
});

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

บทสรุป

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

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

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