Đang phân phát

Đặc điểm quan trọng của Progressive Web Apps là đáng tin cậy; chúng có thể tải nội dung nhanh chóng, thu hút người dùng tương tác và cung cấp phản hồi ngay lập tức, ngay cả trong điều kiện mạng kém. Làm cách nào điều đó có thể? Nhờ có sự kiện của trình chạy dịch vụ fetch.

Sự kiện tìm nạp

Hỗ trợ trình duyệt

  • 40
  • 17
  • 44
  • 11,1

Nguồn

Sự kiện fetch cho phép chúng tôi chặn mọi yêu cầu mạng do PWA tạo ra trong phạm vi của trình chạy dịch vụ, đối với cả yêu cầu cùng nguồn gốc và nhiều nguồn gốc. Ngoài các yêu cầu điều hướng và yêu cầu thành phần, tính năng tìm nạp từ một trình chạy dịch vụ đã cài đặt cho phép hiển thị các lượt truy cập trang sau lần tải đầu tiên của trang web mà không cần lệnh gọi mạng.

Trình xử lý fetch nhận tất cả yêu cầu của một ứng dụng, bao gồm cả URL và tiêu đề HTTP, đồng thời cho phép nhà phát triển ứng dụng quyết định cách xử lý các yêu cầu đó.

Service worker ngồi giữa ứng dụng và mạng.

Service worker của bạn có thể chuyển tiếp yêu cầu đến mạng, phản hồi bằng phản hồi đã được lưu vào bộ nhớ đệm trước đó hoặc tạo phản hồi mới. Bạn có toàn quyền quyết định. Sau đây là một ví dụ đơn giản:

self.addEventListener("fetch", event => {
    console.log(`URL requested: ${event.request.url}`);
});

Phản hồi yêu cầu

Khi có một yêu cầu đến trình chạy dịch vụ, bạn có thể thực hiện 2 yêu cầu; bạn có thể bỏ qua yêu cầu đó để yêu cầu đi đến mạng hoặc bạn có thể phản hồi yêu cầu. Việc phản hồi yêu cầu từ trong trình chạy dịch vụ là cách bạn có thể chọn nội dung và cách thức dữ liệu được trả về PWA của bạn, ngay cả khi người dùng không kết nối mạng.

Để phản hồi một yêu cầu đến, hãy gọi event.respondWith() từ trong trình xử lý sự kiện fetch, như sau:

// fetch event handler in your service worker file
self.addEventListener("fetch", event => {
    const response = .... // a response or a Promise of response
    event.respondWith(response);
});

Bạn phải gọi respondWith() một cách đồng bộ và phải trả về một đối tượng Response (Phản hồi). Tuy nhiên, bạn không thể gọi respondWith() sau khi trình xử lý sự kiện tìm nạp hoàn tất, chẳng hạn như trong một lệnh gọi không đồng bộ. Nếu cần đợi phản hồi hoàn chỉnh, bạn có thể truyền một Promise tới respondWith() sẽ giải quyết bằng một Response.

Tạo câu trả lời

Nhờ API Tìm nạp, bạn có thể tạo các phản hồi HTTP trong mã JavaScript của mình, cũng như các phản hồi đó có thể được lưu vào bộ nhớ đệm bằng API Bộ nhớ đệm và trả về như thể chúng đến từ máy chủ web.

Để tạo một phản hồi, hãy tạo một đối tượng Response mới, đặt nội dung và các tuỳ chọn như trạng thái và tiêu đề:

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)

Phản hồi từ bộ nhớ đệm

Giờ đây, bạn đã biết cách phân phát phản hồi HTTP từ một trình chạy dịch vụ, đã đến lúc sử dụng giao diện Bộ nhớ đệm để lưu trữ thành phần trên thiết bị.

Bạn có thể dùng API bộ nhớ đệm để kiểm tra xem yêu cầu nhận được từ PWA có trong bộ nhớ đệm hay không và nếu có, hãy phản hồi respondWith() kèm theo yêu cầu đó. Để làm được việc đó, trước tiên, bạn cần tìm kiếm trong bộ nhớ đệm. Hàm match() có ở giao diện caches cấp cao nhất sẽ tìm kiếm trên mọi cửa hàng trong nguồn gốc của bạn hoặc trên một đối tượng bộ nhớ đệm đang mở.

Hàm match() nhận một yêu cầu HTTP hoặc URL làm đối số và trả về một lời hứa sẽ giải quyết bằng Phản hồi được liên kết với khoá tương ứng.

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

Chiến lược lưu vào bộ nhớ đệm

Việc chỉ phân phối các tệp từ bộ nhớ đệm của trình duyệt không phù hợp với mọi trường hợp sử dụng. Ví dụ: người dùng hoặc trình duyệt có thể xoá bộ nhớ đệm. Đó là lý do bạn nên xác định các chiến lược của riêng mình để phân phối thành phần cho PWA. Bạn không bị hạn chế ở một chiến lược lưu vào bộ nhớ đệm. Bạn có thể xác định các URL riêng biệt cho từng mẫu URL. Ví dụ: bạn có thể có một chiến lược cho các thành phần giao diện người dùng tối thiểu, một chiến lược khác cho lệnh gọi API và một chiến lược thứ ba cho URL hình ảnh và URL dữ liệu. Để thực hiện việc này, hãy đọc event.request.url trong ServiceWorkerGlobalScope.onfetch và phân tích cú pháp thông qua biểu thức chính quy hoặc Mẫu URL. (Tại thời điểm viết bài, Mẫu URL không được hỗ trợ trên tất cả nền tảng).

Các chiến lược phổ biến nhất là:

Lưu vào bộ nhớ đệm trước tiên
Tìm kiếm phản hồi được lưu vào bộ nhớ đệm trước, sau đó quay lại mạng nếu không tìm thấy phản hồi.
Ưu tiên mạng
Trước tiên, yêu cầu phản hồi từ mạng. Nếu không có phản hồi nào được trả về, hãy kiểm tra phản hồi trong bộ nhớ đệm.
Lỗi thời trong khi xác thực lại
Phân phát phản hồi từ bộ nhớ đệm, còn ở chế độ nền, yêu cầu phiên bản mới nhất và lưu vào bộ nhớ đệm để sử dụng thành phần tiếp theo khi được yêu cầu.
Chỉ mạng
Luôn phản hồi bằng thông tin phản hồi từ mạng hoặc gặp lỗi. Bộ nhớ đệm không bao giờ được tham khảo.
Chỉ lưu vào bộ nhớ đệm
Luôn phản hồi bằng phản hồi từ bộ nhớ đệm hoặc lỗi. Mạng sẽ không bao giờ được tư vấn. Các tài sản sẽ được phân phát bằng chiến lược này phải được thêm vào bộ nhớ đệm trước khi được yêu cầu.

Lưu vào bộ nhớ đệm trước tiên

Khi sử dụng chiến lược này, trình chạy dịch vụ sẽ tìm yêu cầu trùng khớp trong bộ nhớ đệm và trả về Phản hồi tương ứng nếu yêu cầu đó được lưu vào bộ nhớ đệm. Nếu không, thao tác này sẽ truy xuất phản hồi từ mạng (không bắt buộc cập nhật bộ nhớ đệm cho các lệnh gọi sau này). Nếu không có phản hồi bộ nhớ đệm hoặc phản hồi mạng, yêu cầu sẽ gặp lỗi. Vì thành phần phân phát khi không được truy cập vào mạng có xu hướng nhanh hơn, nên chiến lược này sẽ ưu tiên hiệu suất hơn là độ mới.

Chiến lược bộ nhớ đệm đầu tiên

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

Ưu tiên mạng

Chiến lược này là bản sao của chiến lược Bộ nhớ đệm trước tiên; chiến lược kiểm tra xem liệu yêu cầu có thể được thực hiện từ mạng hay không và nếu không thể, hãy cố gắng truy xuất yêu cầu từ bộ nhớ đệm. Thích bộ nhớ đệm trước tiên. Nếu không có phản hồi mạng và phản hồi bộ nhớ đệm, thì yêu cầu sẽ gặp lỗi. Việc nhận phản hồi từ mạng thường chậm hơn so với việc nhận phản hồi từ bộ nhớ đệm. Chiến lược này ưu tiên nội dung cập nhật thay vì hiệu suất.

Chiến lược Mạng Trước tiên

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

Lỗi thời trong khi xác thực lại

Việc cũ trong khi xác thực lại chiến lược sẽ trả về một phản hồi đã lưu vào bộ nhớ đệm ngay lập tức, sau đó kiểm tra mạng để cập nhật và thay thế phản hồi đã lưu vào bộ nhớ đệm nếu tìm thấy một phản hồi. Chiến lược này luôn tạo yêu cầu mạng vì ngay cả khi tìm thấy tài nguyên đã lưu vào bộ nhớ đệm, chiến lược sẽ cố gắng cập nhật nội dung trong bộ nhớ đệm bằng nội dung nhận được từ mạng, để sử dụng phiên bản đã cập nhật trong yêu cầu tiếp theo. Do đó, chiến lược này cung cấp cho bạn một cách để hưởng lợi từ việc phân phối nhanh chiến lược đầu tiên vào bộ nhớ đệm và cập nhật bộ nhớ đệm trong nền.

Lỗi thời trong khi xác thực lại chiến lược

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

Chỉ với mạng

Chiến lược chỉ mạng tương tự như cách hoạt động của trình duyệt mà không có trình chạy dịch vụ hoặc API Bộ nhớ bộ nhớ đệm. Các yêu cầu sẽ chỉ trả về tài nguyên nếu có thể tìm nạp tài nguyên từ mạng. Cách này thường hữu ích đối với các tài nguyên như các yêu cầu API chỉ trực tuyến.

Chiến lược Chỉ mạng

Chỉ bộ nhớ đệm

Chiến lược chỉ lưu vào bộ nhớ đệm đảm bảo rằng các yêu cầu không bao giờ gửi đến mạng; tất cả yêu cầu đến đều được phản hồi bằng một mục bộ nhớ đệm đã điền sẵn. Mã sau đây sử dụng trình xử lý sự kiện fetch với phương thức match của bộ nhớ đệm để chỉ phản hồi bộ nhớ đệm:

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

Chiến lược chỉ lưu vào bộ nhớ đệm.

Chiến lược tuỳ chỉnh

Mặc dù nêu trên là các chiến lược lưu vào bộ nhớ đệm phổ biến, nhưng bạn vẫn chịu trách nhiệm về trình chạy dịch vụ và cách xử lý các yêu cầu. Nếu không có nguồn nào trong số này phù hợp với nhu cầu của bạn, hãy tạo tài khoản của riêng bạn.

Ví dụ: bạn có thể sử dụng chiến lược ưu tiên mạng với thời gian chờ để ưu tiên nội dung cập nhật, nhưng chỉ khi phản hồi xuất hiện trong ngưỡng bạn đặt. Bạn cũng có thể hợp nhất một phản hồi đã lưu vào bộ nhớ đệm với một phản hồi mạng và tạo một phản hồi phức tạp từ trình chạy dịch vụ.

Đang cập nhật nội dung

Việc duy trì cập nhật các thành phần được lưu vào bộ nhớ đệm của PWA có thể là một thách thức. Mặc dù việc cũ trong khi xác thực lại chiến lược là một cách để làm như vậy, nhưng đó không phải là cách duy nhất. Trong chương Cập nhật, bạn sẽ tìm hiểu các kỹ thuật giúp nội dung và thành phần của ứng dụng luôn được cập nhật.

Tài nguyên