Đang phân phát

Một khía cạnh chính của Ứng dụng web tiến bộ là chúng đáng tin cậy; chúng có thể tải nội dung nhanh chóng, thu hút người dùng và cung cấp phản hồi ngay lập tức, ngay cả khi điều kiện mạng kém. Làm thế nào có thể như vậy? Nhờ có sự kiện fetch của trình chạy dịch vụ.

Hỗ trợ trình duyệt

  • Chrome: 40.
  • Cạnh: 17.
  • Firefox: 44.
  • Safari: 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 đưa 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à yêu cầu có nhiều nguồn gốc. Ngoài các yêu cầu điều hướng và nội dung, việc 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 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ày 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 một yêu cầu tới mạng, phản hồi bằng một phản hồi đã lưu vào bộ nhớ đệm trước đó hoặc tạo một 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 được gửi đến trình chạy dịch vụ, bạn có thể làm hai việc sau: bạn có thể bỏ qua để cho phép truy cập vào mạng hoặc phản hồi lại. Bạn có thể phản hồi yêu cầu của trình chạy dịch vụ bằng cách chọn nội dung và cách trả về ứng dụng web tiến bộ (PWA), 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ề đố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 đợi phản hồi hoàn chỉnh, bạn có thể chuyển Lời hứa đến respondWith() để phân giải bằng Phản hồi.

Tạo câu trả lời

Nhờ API Tìm nạp, bạn có thể tạo phản hồi HTTP trong mã JavaScript của mình và những phản hồi đó có thể được lưu vào bộ nhớ đệm bằng cách sử dụng API Lưu trữ bộ nhớ đệm và 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

Bây giờ, 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ớ vào bộ nhớ đệm để lưu trữ tài sản trên thiết bị.

Bạn có thể dùng API lưu trữ 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() kèm theo 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ó ở giao diện caches cấp cao nhất giúp tìm kiếm tất cả các kho lưu trữ trong gốc của bạn hoặc trên một đối tượng bộ nhớ đệm mở.

Hàm match() nhận một yêu cầu HTTP hoặc một URL làm đối số và trả về một hàm hứa hẹn phân giải 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

Không phải trường hợp sử dụng nào cũng phù hợp với việc chỉ phân phát tệp từ bộ nhớ đệm của trình duyệt. Ví dụ: người dùng hoặc trình duyệt có thể xoá bộ nhớ đệm. Vì vậy, 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ị 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 biến riêng 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 tối thiểu trên giao diện người dùng, một chiến lược khác cho lệnh gọi API và 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, Mẫu URL không được hỗ trợ trên tất cả nền tảng).

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

Lưu vào bộ nhớ đệm trước tiên
Trước tiên, hãy tìm kiếm một phản hồi được lưu vào bộ nhớ đệm rồi quay lại mạng nếu không tìm thấy phản hồi đó.
Ư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 thời trong khi xác thực lại
Phân phát một phản hồi từ bộ nhớ đệm, đồng thời ở chế độ nền, yêu cầu phiên bản mới nhất và lưu vào bộ nhớ đệm để dùng 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 của mạng hoặc báo lỗi. Bộ nhớ đệm này không bao giờ được tham khảo.
Chỉ bộ nhớ đệm
Luôn trả lời bằng một phản hồi từ bộ nhớ đệm hoặc nhận được lỗi. Mạng này sẽ không bao giờ được tham khảo ý kiến. Những nội dung 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 phù hợp trong bộ nhớ đệm và trả về Phản hồi tương ứng nếu đã được lưu vào bộ nhớ đệm. Nếu không, trình duyệt 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 trong tương lai). Nếu không có phản hồi bộ nhớ đệm và phản hồi mạng, thì 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 chuyển đến 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 Bộ nhớ đệm trước 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 kết nối mạng

Chiến lược này chính là bản sao của chiến lược Cache First (Bộ nhớ đệm trước tiên); hệ thống 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ể, cố gắng truy xuất yêu cầu từ bộ nhớ đệm. Thích bộ nhớ đệm trước. Nếu không có phản hồi mạng lẫn 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 Ưu tiên mạng lưới

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

Đã cũ trong khi xác thực lại

Chiến lược lỗi thời trong khi xác thực lại sẽ trả về ngay phản hồi đã lưu vào bộ nhớ đệm, sau đó kiểm tra mạng để tìm bản cập nhật và thay thế phản hồi đã lưu vào bộ nhớ đệm nếu tìm thấy phản hồi đó. 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 trong bộ nhớ đệm, chiến lược này sẽ cố gắng cập nhật nội dung trong bộ nhớ đệm với 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át nhanh chiến lược bộ nhớ đệm trước tiên và cập nhật bộ nhớ đệm ở chế độ nền.

Chiến lược cũ trong khi xác thực lại

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ỉ kết nối mạng tương tự như cách các trình duyệt hoạt động mà không cần trình chạy dịch vụ hoặc API 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 được từ mạng. Điều này thường hữu ích cho 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ỉ bộ nhớ đệm đảm bảo rằng các yêu cầu không bao giờ đi đến mạng; tất cả các yêu cầu đến sẽ được phản hồi bằng mục bộ nhớ đệm được điền sẵn. Đoạ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ù ở 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ụ của mình và cách xử lý yêu cầu. Nếu những cách trên không phù hợp với nhu cầu của bạn, hãy tự tạo.

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 đượ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ừ trình chạy dịch vụ.

Đang cập nhật tài sản

Việc 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ù chiến lược cũ trong khi xác thực lại là một cách để thực hiện, 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 khác nhau để luôn cập nhật nội dung và thành phần của ứng dụng.

Tài nguyên