กำลังให้บริการ

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

เหตุการณ์การดึงข้อมูล

Browser Support

  • Chrome: 40.
  • Edge: 17.
  • Firefox: 44.
  • Safari: 11.1.

Source

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

แฮนเดิลเลอร์ fetch จะรับคำขอทั้งหมดจากแอป ซึ่งรวมถึง URL และส่วนหัวของ HTTP และช่วยให้นักพัฒนาแอปตัดสินใจได้ว่าจะประมวลผลคำขอเหล่านั้นอย่างไร

Service Worker จะอยู่ระหว่างไคลเอ็นต์กับเครือข่าย

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() ไม่ได้หลังจากที่ตัวแฮนเดิลเหตุการณ์การดึงข้อมูลเสร็จสิ้นแล้ว เช่น ภายในฟังก์ชันเรียกแบบไม่พร้อมกัน หากต้องการรอการตอบกลับที่สมบูรณ์ คุณสามารถส่ง Promise ไปยัง respondWith() ที่จะแก้ไขด้วย Response

การสร้างคำตอบ

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อินเทอร์เฟซระดับบนสุดจะค้นหาร้านค้าทั้งหมดในต้นทางหรือในออบเจ็กต์แคชที่เปิดอยู่รายการเดียว

match() ฟังก์ชันจะรับคำขอ HTTP หรือ URL เป็นอาร์กิวเมนต์ และแสดงผล Promise ที่แก้ไขด้วยการตอบกลับที่เชื่อมโยงกับคีย์ที่เกี่ยวข้อง

// 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 ที่แตกต่างกันได้ เช่น คุณอาจมีกลยุทธ์หนึ่งสำหรับชิ้นงาน UI ขั้นต่ำ อีกกลยุทธ์หนึ่งสำหรับการเรียก API และอีกกลยุทธ์หนึ่งสำหรับ URL ของรูปภาพและข้อมูล หากต้องการทำเช่นนี้ ให้อ่านevent.request.urlในServiceWorkerGlobalScope.onfetch แล้วแยกวิเคราะห์ผ่านนิพจน์ทั่วไปหรือรูปแบบ URL (ขณะที่เขียนบทความนี้ บางแพลตฟอร์มยังไม่รองรับรูปแบบ URL)

กลยุทธ์ที่พบบ่อยที่สุดมีดังนี้

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

แคชก่อน

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

กลยุทธ์ &quot;แคชก่อน&quot;

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);
     }
   )
  )
});

เครือข่ายเป็นอันดับแรก

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

กลยุทธ์ &quot;เครือข่ายต้องมาเป็นอันดับแรก&quot;

self.addEventListener("fetch", event => {
   event.respondWith(
     fetch(event.request)
     .catch(error => {
       return caches.match(event.request) ;
     })
   );
});

แสดงขณะที่ไม่มีอัปเดต

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

กลยุทธ์ &quot;ล้าสมัยขณะตรวจสอบซ้ำ&quot;

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

แหล่งข้อมูล