Đang phân phát

Một khía cạnh quan trọng của Ứng dụng web tiến bộ là độ tin cậy; ứng dụng này có thể tải nhanh các thành phần, giúp người dùng tương tác và phản hồi ngay lập tức, ngay cả trong điều kiện mạng kém. Sao có thể như vậy được? Nhờ sự kiện fetch của worker dịch vụ.

Sự kiện tìm nạp

Browser Support

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

Source

Sự kiện fetch cho phép chúng ta chặn mọi yêu cầu mạng do PWA thực hiện trong phạm vi của worker dịch vụ, cho cả yêu cầu cùng nguồn gốc và yêu cầu trên nhiều nguồn gốc. Ngoài các yêu cầu điều hướng và tài sản, việc tìm nạp từ một worker 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 từ 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 đó.

Trình chạy dịch vụ nằm giữa ứng dụng và mạng.

Trình chạy dịch vụ 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 đã lưu vào bộ nhớ đệm trước đó hoặc tạo phản hồi mới. Quyền quyết định là ở bạn! 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 một yêu cầu đến trình chạy dịch vụ, bạn có thể làm hai việc: bỏ qua yêu cầu đó để yêu cầu đó chuyển đến mạng hoặc phản hồi yêu cầu đó. Việc phản hồi các yêu cầu từ trong worker dịch vụ là cách bạn có thể chọn nội dung và cách trả về nội dung đó cho PWA, ngay cả khi người dùng đang ngoại tuyến.

Để phản hồi một yêu cầu đến, hãy gọi event.respondWith() từ bên 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 lệnh gọi không đồng bộ. Nếu cần chờ phản hồi đầy đủ, bạn có thể truyền một Lời hứa đến respondWith() để giải quyết bằng một Phản hồi.

Tạo câu trả lời

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

Để tạo 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 worker dịch vụ, đã đến lúc sử dụng giao diện Lưu trữ đệm để lưu trữ các thành phần trên thiết bị.

Bạn có thể sử 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. Nếu có, hãy phản hồi respondWith() bằng yêu cầu đó. Để làm việc đó, trước tiên, bạn cần tìm kiếm trong bộ nhớ đệm. Hàm match() có trong giao diện caches cấp cao nhất sẽ tìm kiếm tất cả các 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 phân giải với Phản hồi 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át 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 chiến lược riêng để phân phối thành phần cho PWA. Bạn không bị giới hạn ở một chiến lược lưu vào bộ nhớ đệm. Bạn có thể xác định các giá trị khác nhau cho các mẫu URL khác nhau. 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 các lệnh gọi API và một chiến lược thứ ba cho URL hình ảnh và 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 một số nền tảng).

Sau đây là các chiến lược phổ biến nhất:

Bộ nhớ đệm trước
Trước tiên, hãy tìm kiếm phản hồi được lưu vào bộ nhớ đệm và quay lại mạng nếu không tìm thấy phản hồi nào.
Ưu tiên mạng
Yêu cầu phản hồi từ mạng trước và 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 cũ trong khi xác thực lại
Phân phát phản hồi từ bộ nhớ đệm, trong khi ở chế độ nền, yêu cầu phiên bản mới nhất và lưu phiên bản đó vào bộ nhớ đệm cho lần tiếp theo tài sản được yêu cầu.
Chỉ mạng
Luôn trả lời bằng 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ỉ bộ nhớ đệm
Luôn trả lời bằng phản hồi từ bộ nhớ đệm hoặc gặp lỗi. Mạng này sẽ không bao giờ được tham khảo. Các thành phầ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

Khi sử dụng chiến lược này, trình chạy dịch vụ sẽ tìm yêu cầu phù hợ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, ứng dụng sẽ truy xuất phản hồi từ mạng (không bắt buộc phải cập nhật bộ nhớ đệm cho các lệnh gọi trong tương lai). 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ì việc phân phát thành phần mà không cần truy cập vào mạng có xu hướng nhanh hơn, nên chiến lược này ưu tiên hiệu suất hơn là độ mới.

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

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 Ưu tiên bộ nhớ đệm; chiến lược này kiểm tra xem 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. Trước tiên, hãy xoá bộ nhớ đệm. Nếu không có phản hồi mạng hoặc phản hồi bộ nhớ đệm, 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 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 ưu tiên mạng

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

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

Chiến lược cũ trong khi xác thực lại trả về phản hồi được lưu vào bộ nhớ đệm ngay lập tức, sau đó kiểm tra mạng để cập nhật, thay thế phản hồi được lưu vào bộ nhớ đệm nếu tìm thấy. Chiến lược này luôn tạo một yêu cầu mạng, vì ngay cả khi tìm thấy tài nguyên được lưu vào bộ nhớ đệm, chiến lược này 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 giúp bạn tận dụng việc phân phát nhanh của chiến lược ưu tiên bộ nhớ đệm và cập nhật bộ nhớ đệm ở chế độ nền.

Chiến lược xác thực lại khi hết hạn

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 trình duyệt hoạt động mà không có worker dịch vụ hoặc API Bộ nhớ đệm. Yêu cầu sẽ chỉ trả về một tài nguyên nếu có thể tìm nạp tài nguyên đó từ mạng. Điều này thường hữu ích cho các tài nguyên như yêu cầu API chỉ dành cho trực tuyến.

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

Chỉ bộ nhớ đệm

Chiến lược chỉ bộ nhớ đệm đảm bảo rằng các yêu cầu không bao giờ được 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 được đ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ù những chiến lược trên là các chiến lược lưu vào bộ nhớ đệm phổ biến, nhưng bạ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ó cách nào phù hợp với nhu cầu của bạn, hãy tạo cách riêng.

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 mà bạn đặt. Bạn cũng có thể hợp nhất phản hồi được lưu vào bộ nhớ đệm với phản hồi mạng và tạo một phản hồi phức tạp từ worker dịch vụ.

Cập nhật thành phần

Việc cập nhật các thành phần lưu vào bộ nhớ đệm của PWA có thể là một thách thức. Mặc dù chiến lược xác thực lại khi hết hạn là một cách để làm việc nà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 để cập nhật nội dung và thành phần của ứng dụng.

Tài nguyên