แง่มุมสำคัญของ Progressive Web App คือมีความเสถียร แอปสามารถโหลดเนื้อหาได้อย่างรวดเร็ว ทำให้ผู้ใช้มีส่วนร่วมและแสดงความคิดเห็นได้ทันที แม้ในสภาพเครือข่ายไม่ดี เป็นไปได้อย่างไร มีเหตุการณ์ fetch
สำหรับ Service Worker
เหตุการณ์การดึงข้อมูล
เหตุการณ์ fetch
ช่วยให้เราสกัดกั้นทุกคำขอเครือข่ายที่ PWA สร้างขึ้นในขอบเขตของ Service Worker สำหรับทั้งคำขอต้นทางเดียวกันและข้ามต้นทาง นอกจากการนำทางและคำขอเนื้อหาแล้ว การดึงข้อมูลจาก Service Worker ที่ติดตั้งไว้จะทำให้การเข้าชมหน้าเว็บหลังจากการโหลดครั้งแรกของเว็บไซต์แสดงผลได้โดยไม่ต้องใช้การเรียกใช้เครือข่าย
ตัวแฮนเดิล fetch
จะได้รับคำขอทั้งหมดจากแอป ซึ่งรวมถึง URL และส่วนหัว HTTP และช่วยให้นักพัฒนาแอปเลือกวิธีประมวลผลคำขอได้
Service Worker สามารถส่งต่อคำขอไปยังเครือข่าย ตอบกลับด้วยการตอบกลับที่แคชไว้ก่อนหน้านี้ หรือสร้างการตอบกลับใหม่ได้ ทั้งนี้ขึ้นอยู่กับการตัดสินใจของคุณ ต่อไปนี้เป็นตัวอย่างง่ายๆ
self.addEventListener("fetch", event => {
console.log(`URL requested: ${event.request.url}`);
});
การตอบกลับคำขอ
เมื่อมีคำขอเข้ามาใน Service Worker คุณสามารถดำเนินการได้ 2 อย่างคือ ไม่สนใจคำขอ จะทำให้ระบบส่งคำสั่งนั้นไปที่เครือข่าย หรือจะตอบกลับก็ได้ การตอบกลับคำขอจากภายใน Service Worker คือวิธีที่คุณสามารถเลือกสิ่งที่ทำได้และวิธีการส่งคืนไปยัง PWA ของคุณแม้ว่าผู้ใช้จะออฟไลน์อยู่ก็ตาม
หากต้องการตอบกลับคำขอที่เข้ามาใหม่ ให้โทรหา event.respondWith()
จากภายในเครื่องจัดการเหตุการณ์ fetch
ดังต่อไปนี้
// fetch event handler in your service worker file
self.addEventListener("fetch", event => {
const response = .... // a response or a Promise of response
event.respondWith(response);
});
คุณต้องเรียกใช้ respondWith()
แบบพร้อมกันและต้องแสดงผลออบเจ็กต์ Response แต่คุณจะเรียกใช้ respondWith()
ไม่ได้หลังจากเครื่องจัดการเหตุการณ์การดึงข้อมูลเสร็จสิ้น เช่น ในการเรียกใช้แบบไม่พร้อมกัน หากต้องรอการตอบกลับที่สมบูรณ์ คุณสามารถส่งคำมั่นสัญญาไปยัง respondWith()
ที่แก้ไขปัญหาด้วยการตอบกลับได้
การสร้างคำตอบ
คุณสามารถใช้ Fetch API เพื่อสร้างการตอบสนอง HTTP ในโค้ด JavaScript และสามารถแคชการตอบกลับเหล่านั้นโดยใช้ Cache Storage API และแสดงผลเหมือนกับว่ามาจากเว็บเซิร์ฟเวอร์
ในการสร้างคำตอบ ให้สร้างออบเจ็กต์ Response
ใหม่ โดยตั้งค่าเนื้อความและตัวเลือกต่างๆ เช่น สถานะและส่วนหัว ดังนี้
const simpleResponse = new Response("Body of the HTTP response");
const options = {
status: 200,
headers: {
'Content-type': 'text/html'
}
};
const htmlResponse = new Response("<b>HTML</b> content", options)
การตอบกลับจากแคช
เมื่อคุณทราบวิธีแสดงการตอบกลับ HTTP จาก Service Worker แล้ว ก็ถึงเวลาใช้อินเทอร์เฟซการแคชพื้นที่เก็บข้อมูลเพื่อจัดเก็บเนื้อหาในอุปกรณ์
คุณสามารถใช้ Cache Storage API เพื่อตรวจสอบว่าคําขอที่ได้รับจาก PWA อยู่ในแคชหรือไม่ และหากมี ให้ตอบกลับ respondWith()
โดยใช้คำขอดังกล่าว
ในการทำเช่นนั้น คุณต้องค้นหาภายในแคชก่อน ฟังก์ชัน match()
ซึ่งมีอยู่ที่อินเทอร์เฟซ caches
ระดับบนสุดจะค้นหา Store ทั้งหมดในต้นทางหรือในออบเจ็กต์แคชที่เปิดอยู่รายการเดียว
ฟังก์ชัน match()
ได้รับคำขอ HTTP หรือ URL เป็นอาร์กิวเมนต์ และแสดงผลสัญญาที่แปลงข้อมูลด้วยการตอบกลับที่เชื่อมโยงกับคีย์ที่เกี่ยวข้อง
// Global search on all caches in the current origin
caches.match(urlOrRequest).then(response => {
console.log(response ? response : "It's not in the cache");
});
// Cache-specific search
caches.open("pwa-assets").then(cache => {
cache.match(urlOrRequest).then(response => {
console.log(response ? response : "It's not in the cache");
});
});
กลยุทธ์การแคช
การแสดงไฟล์จากแคชของเบราว์เซอร์เท่านั้นไม่เหมาะกับกรณีการใช้งานทั้งหมด ตัวอย่างเช่น ผู้ใช้หรือเบราว์เซอร์สามารถนำแคชออกได้ คุณจึงควรกำหนดกลยุทธ์ในการส่งเนื้อหาสำหรับ PWA
คุณไม่ได้ถูกจำกัดให้ใช้กลยุทธ์การแคชเพียงกลยุทธ์เดียว ให้กำหนด URL ที่แตกต่างกันสำหรับรูปแบบ URL ที่แตกต่างกัน ตัวอย่างเช่น คุณอาจมีกลยุทธ์หนึ่งสำหรับเนื้อหา UI ขั้นต่ำ อีกกลยุทธ์หนึ่งสำหรับการเรียก API และอีกกลยุทธ์หนึ่งสำหรับ URL รูปภาพและข้อมูล
ในการดำเนินการนี้ ให้อ่าน event.request.url
ใน ServiceWorkerGlobalScope.onfetch
และแยกวิเคราะห์ผ่านนิพจน์ทั่วไปหรือรูปแบบ URL (ในขณะที่เขียน รูปแบบ URL ยังไม่ได้รับการสนับสนุนในบางแพลตฟอร์ม)
กลยุทธ์ที่ใช้กันมากที่สุด ได้แก่
- แคชก่อน
- ค้นหาการตอบกลับที่แคชไว้ก่อน แล้วจึงกลับไปยังเครือข่ายหากไม่พบ
- เครือข่ายก่อน
- ขอการตอบสนองจากเครือข่ายก่อน แล้วตรวจสอบการตอบกลับในแคชหากไม่มีการแสดงผล
- ไม่มีอัปเดตขณะตรวจสอบความถูกต้องอีกครั้ง
- แสดงการตอบกลับจากแคช ขณะที่ในเบื้องหลังจะขอเวอร์ชันล่าสุดและบันทึกลงในแคชในครั้งถัดไปที่มีการขอเนื้อหา
- เครือข่ายเท่านั้น
- ตอบกลับด้วยการตอบกลับจากเครือข่ายหรือแจ้งข้อผิดพลาดทุกครั้ง ระบบจะไม่ปรึกษาแคชเลย
- แคชเท่านั้น
- ตอบกลับด้วยการตอบกลับจากแคชหรือข้อผิดพลาดเสมอ จะไม่มีการปรึกษาเกี่ยวกับเครือข่ายโดยเด็ดขาด ต้องเพิ่มชิ้นงานที่จะแสดงโดยใช้กลยุทธ์นี้ลงในแคชก่อนจะมีการขอ
แคชก่อน
เมื่อใช้กลยุทธ์นี้ โปรแกรมทำงานของบริการจะค้นหาคำขอที่ตรงกันในแคชและแสดงผลการตอบกลับที่เกี่ยวข้องหากมีการแคชไว้ มิเช่นนั้น ระบบจะดึงการตอบกลับจากเครือข่าย (ตัวเลือกอัปเดตแคชสำหรับการเรียกในอนาคต) หากไม่มีการตอบกลับแคชหรือการตอบสนองของเครือข่าย คำขอจะเกิดข้อผิดพลาด เนื่องจากการแสดงชิ้นงานโดยไม่ไปยังเครือข่ายมักจะทำงานได้เร็วกว่า กลยุทธ์นี้จึงให้ความสำคัญกับประสิทธิภาพมากกว่าความใหม่
self.addEventListener("fetch", event => {
event.respondWith(
caches.match(event.request)
.then(cachedResponse => {
// It can update the cache to serve updated content on the next request
return cachedResponse || fetch(event.request);
}
)
)
});
เครือข่ายก่อน
กลยุทธ์นี้เป็นการมิเรอร์ของกลยุทธ์ Cache First โดยจะตรวจสอบว่าสามารถดำเนินการตามคำขอจากเครือข่ายได้หรือไม่ และหากทำไม่ได้ จะลองเรียกข้อมูลจากแคช กดชอบแคชก่อน หากไม่มีการตอบสนองของเครือข่ายหรือการตอบสนองแคช คำขอจะเกิดข้อผิดพลาด การได้รับการตอบสนองจากเครือข่ายมักจะช้ากว่าการรับข้อมูลจากแคช กลยุทธ์นี้ให้ความสำคัญกับเนื้อหาที่อัปเดตมากกว่าประสิทธิภาพ
self.addEventListener("fetch", event => {
event.respondWith(
fetch(event.request)
.catch(error => {
return caches.match(event.request) ;
})
);
});
ไม่มีอัปเดตขณะตรวจสอบความถูกต้องอีกครั้ง
ไม่มีการอัปเดตขณะตรวจสอบกลยุทธ์อีกครั้งจะแสดงการตอบกลับที่แคชไว้ทันที จากนั้นจะตรวจสอบเครือข่ายเพื่อหาการอัปเดต ซึ่งจะแทนที่การตอบกลับที่แคชไว้ หากพบ กลยุทธ์นี้จะส่งคำขอเครือข่ายเสมอ เพราะแม้ว่าจะพบทรัพยากรที่แคชไว้ แต่ระบบจะพยายามอัปเดตสิ่งที่อยู่ในแคชด้วยข้อมูลที่ได้รับจากเครือข่าย เพื่อใช้เวอร์ชันที่อัปเดตในคำขอถัดไป กลยุทธ์นี้จึงให้ประโยชน์แก่คุณในการใช้กลยุทธ์แบบแคชแรกและอัปเดตแคชในเบื้องหลังอย่างรวดเร็ว
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request).then(cachedResponse => {
const networkFetch = fetch(event.request).then(response => {
// update the cache with a clone of the network response
const responseClone = response.clone()
caches.open(url.searchParams.get('name')).then(cache => {
cache.put(event.request, responseClone)
})
return response
}).catch(function (reason) {
console.error('ServiceWorker fetch failed: ', reason)
})
// prioritize cached response over network
return cachedResponse || networkFetch
}
)
)
})
เครือข่ายเท่านั้น
กลยุทธ์แบบเครือข่ายเท่านั้นจะคล้ายกับลักษณะการทำงานของเบราว์เซอร์เมื่อใช้ Service Worker หรือ Cache Storage API คำขอจะส่งกลับทรัพยากรในกรณีที่ดึงข้อมูลจากเครือข่ายได้เท่านั้น วิธีนี้มีประโยชน์สำหรับทรัพยากรอย่างคำขอ API ทางออนไลน์เท่านั้น
แคชเท่านั้น
กลยุทธ์ที่ใช้แคชเท่านั้นช่วยให้แน่ใจว่าคำขอจะไม่ส่งไปที่เครือข่าย คำขอที่เข้ามาทั้งหมดจะได้รับการตอบสนองด้วยรายการแคชที่ป้อนข้อมูลไว้ล่วงหน้า โค้ดต่อไปนี้ใช้เครื่องจัดการเหตุการณ์ fetch
กับเมธอด match
ของพื้นที่เก็บข้อมูลแคชเพื่อตอบกลับแคชเท่านั้น
self.addEventListener("fetch", event => {
event.respondWith(caches.match(event.request));
});
กลยุทธ์ที่กำหนดเอง
แม้ว่าวิธีการข้างต้นจะเป็นกลยุทธ์การแคชที่พบได้ทั่วไป แต่คุณเป็นผู้รับผิดชอบ Service Worker และวิธีจัดการคำขอ หากไม่มีวิธีที่ตรงกับความต้องการของคุณเลย ให้สร้างขึ้นมา
เช่น คุณสามารถใช้กลยุทธ์แบบเครือข่ายเป็นหลักซึ่งมีระยะหมดเวลาเพื่อจัดลำดับความสำคัญของเนื้อหาที่อัปเดต แต่ใช้เฉพาะในกรณีที่การตอบกลับนั้นปรากฏในเกณฑ์ที่คุณตั้งไว้ นอกจากนี้คุณยังผสานการตอบกลับที่แคชไว้เข้ากับการตอบกลับของเครือข่ายและสร้างการตอบสนองที่ซับซ้อนจาก Service Worker ได้ด้วย
กำลังอัปเดตเนื้อหา
การอัปเดตเนื้อหาที่แคชไว้ของ PWA ให้เป็นปัจจุบันอยู่เสมออาจเป็นเรื่องท้าทาย แม้ว่าการใช้กลยุทธ์เก่าขณะตรวจสอบความถูกต้องของกลยุทธ์อีกครั้งเป็นวิธีหนึ่ง แต่ก็ไม่ได้เป็นเช่นนั้นจริง ในส่วนการอัปเดต คุณจะได้เรียนรู้เทคนิคต่างๆ ในการอัปเดตเนื้อหาและชิ้นงานของแอปอยู่เสมอ