Chiến lược lưu vào bộ nhớ đệm của nhân viên dịch vụ

Cho đến bây giờ, mới chỉ có các lượt đề cập và các đoạn mã nhỏ của Giao diện Cache. Để sử dụng trình chạy dịch vụ một cách hiệu quả, cần áp dụng một hoặc nhiều chiến lược lưu vào bộ nhớ đệm, yêu cầu người dùng phải làm quen với giao diện Cache.

Chiến lược lưu vào bộ nhớ đệm là hoạt động tương tác giữa sự kiện fetch của trình chạy dịch vụ và giao diện Cache. Cách viết chiến lược lưu vào bộ nhớ đệm phụ thuộc vào ví dụ: chúng tôi có thể ưu tiên xử lý các yêu cầu về tài sản tĩnh khác với tài liệu, và điều này ảnh hưởng đến cách cấu trúc chiến lược lưu vào bộ nhớ đệm.

Trước khi chúng ta tìm hiểu về các chiến lược, hãy dành chút thời gian để nói về đặc điểm của giao diện Cache, và xem nhanh một số phương thức mà công cụ này cung cấp để quản lý bộ nhớ đệm của trình chạy dịch vụ.

Giao diện Cache so với bộ nhớ đệm HTTP

Nếu trước đây bạn chưa từng làm việc với giao diện Cache, bạn có thể nghĩ rằng điều đó giống như hoặc ít nhất là liên quan đến bộ nhớ đệm HTTP. Tuy nhiên, trường hợp này không đúng.

  • Giao diện Cache là một cơ chế lưu vào bộ nhớ đệm hoàn toàn tách biệt với bộ nhớ đệm HTTP.
  • Bất kỳ thứ gì Cache-Control mà bạn sử dụng để tác động đến bộ nhớ đệm HTTP sẽ không ảnh hưởng đến nội dung được lưu trữ trong giao diện Cache.

Điều này giúp bạn hình dung bộ nhớ đệm của trình duyệt được phân lớp. Bộ nhớ đệm HTTP là một bộ nhớ đệm cấp thấp do các cặp khoá-giá trị điều khiển, có lệnh được thể hiện trong tiêu đề HTTP.

Ngược lại, giao diện Cache là bộ nhớ đệm cấp cao do API JavaScript điều khiển. Điều này mang lại tính linh hoạt cao hơn so với khi sử dụng cặp khoá-giá trị HTTP tương đối đơn giản. là một nửa trong số những điều giúp tạo ra chiến lược lưu vào bộ nhớ đệm. Một số phương thức API quan trọng liên quan đến bộ nhớ đệm của trình chạy dịch vụ là:

  • CacheStorage.open để tạo một phiên bản Cache mới.
  • Cache.addCache.put để lưu trữ phản hồi mạng trong bộ nhớ đệm của trình chạy dịch vụ.
  • Cache.match để tìm phản hồi được lưu vào bộ nhớ đệm trong thực thể Cache.
  • Cache.delete để xoá phản hồi đã lưu vào bộ nhớ đệm khỏi thực thể Cache.

Đây chỉ là một vài trong số đó. Có những phương pháp hữu ích khác, nhưng đây là những phương pháp cơ bản mà bạn sẽ thấy trong phần sau của hướng dẫn này.

Sự kiện đơn giản ở fetch

Nửa còn lại của chiến lược lưu vào bộ nhớ đệm là Sự kiện fetch. Cho đến bây giờ, trong tài liệu này, bạn đã nghe nói một chút về việc "chặn yêu cầu mạng", và sự kiện fetch bên trong một trình chạy dịch vụ là nơi điều này xảy ra:

// Establish a cache name
const cacheName = 'MyFancyCacheName_v1';

self.addEventListener('install', (event) => {
  event.waitUntil(caches.open(cacheName));
});

self.addEventListener('fetch', async (event) => {
  // Is this a request for an image?
  if (event.request.destination === 'image') {
    // Open the cache
    event.respondWith(caches.open(cacheName).then((cache) => {
      // Respond with the image from the cache or from the network
      return cache.match(event.request).then((cachedResponse) => {
        return cachedResponse || fetch(event.request.url).then((fetchedResponse) => {
          // Add the network response to the cache for future visits.
          // Note: we need to make a copy of the response to save it in
          // the cache and use the original as the request response.
          cache.put(event.request, fetchedResponse.clone());

          // Return the network response
          return fetchedResponse;
        });
      });
    }));
  } else {
    return;
  }
});

Đây là một ví dụ về đồ chơi và quy trình mà bạn có thể thấy ngay trong thực tế, nhưng đây là một cơ chế cung cấp thông tin sơ lược về những việc mà worker có thể làm. Đoạn mã trên sẽ thực hiện những việc sau:

  1. Hãy kiểm tra thuộc tính destination của yêu cầu để xem đây có phải là yêu cầu hình ảnh hay không.
  2. Nếu hình ảnh nằm trong bộ nhớ đệm của trình chạy dịch vụ, hãy phân phát hình ảnh đó từ đó. Nếu không, hãy tìm nạp hình ảnh từ mạng lưu trữ phản hồi trong bộ nhớ đệm và trả về phản hồi mạng.
  3. Tất cả các yêu cầu khác được chuyển qua trình chạy dịch vụ mà không có tương tác với bộ nhớ đệm.

Đối tượng event của lần tìm nạp chứa Tài sản request một số thông tin hữu ích giúp bạn xác định loại của mỗi yêu cầu:

  • url! đây là URL cho yêu cầu mạng hiện đang được sự kiện fetch xử lý.
  • method! là phương thức yêu cầu (ví dụ: GET hoặc POST).
  • mode! Mô tả chế độ của yêu cầu. Giá trị của 'navigate' thường được dùng để phân biệt yêu cầu đối với tài liệu HTML với các yêu cầu khác.
  • destination! mô tả loại nội dung được yêu cầu theo cách tránh sử dụng đuôi tệp của nội dung được yêu cầu.

Một lần nữa, không đồng bộ là tên của trò chơi. Bạn sẽ nhớ rằng sự kiện install cung cấp event.waitUntil một phương thức hứa hẹn và chờ giải quyết trước khi tiếp tục kích hoạt. Sự kiện fetch cung cấp sự kiện tương tự Phương thức event.respondWith mà bạn có thể sử dụng để trả về kết quả của sự kiện không đồng bộ Yêu cầu fetch hoặc phản hồi được trả về bởi giao diện Cache Phương pháp match.

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

Bây giờ, bạn đã quen thuộc với các thực thể Cache và trình xử lý sự kiện fetch, bạn đã sẵn sàng tìm hiểu sâu hơn về một số chiến lược lưu vào bộ nhớ đệm của trình chạy dịch vụ. Mặc dù thực tế thì khả năng là vô hạn, hướng dẫn này sẽ bám sát các chiến lược đi kèm với Workbox, để bạn có thể nắm được những gì đang diễn ra trong nội bộ của Workbox.

Chỉ bộ nhớ đệm

Hiển thị luồng từ trang, trình chạy dịch vụ, đến bộ nhớ đệm.

Hãy bắt đầu với chiến lược lưu vào bộ nhớ đệm đơn giản mà chúng ta sẽ gọi là "Chỉ bộ nhớ đệm". Chỉ là: khi service worker kiểm soát trang, các yêu cầu so khớp sẽ chỉ được chuyển vào bộ nhớ đệm. Điều này có nghĩa là mọi nội dung được lưu vào bộ nhớ đệm sẽ cần được lưu trước vào bộ nhớ đệm để mẫu có thể hoạt động. và những thành phần đó sẽ không bao giờ được cập nhật trong bộ nhớ đệm cho đến khi trình chạy dịch vụ được cập nhật.

// Establish a cache name
const cacheName = 'MyFancyCacheName_v1';

// Assets to precache
const precachedAssets = [
  '/possum1.jpg',
  '/possum2.jpg',
  '/possum3.jpg',
  '/possum4.jpg'
];

self.addEventListener('install', (event) => {
  // Precache assets on install
  event.waitUntil(caches.open(cacheName).then((cache) => {
    return cache.addAll(precachedAssets);
  }));
});

self.addEventListener('fetch', (event) => {
  // Is this one of our precached assets?
  const url = new URL(event.request.url);
  const isPrecachedRequest = precachedAssets.includes(url.pathname);

  if (isPrecachedRequest) {
    // Grab the precached asset from the cache
    event.respondWith(caches.open(cacheName).then((cache) => {
      return cache.match(event.request.url);
    }));
  } else {
    // Go to the network
    return;
  }
});

Ở trên, một mảng các thành phần được lưu trước vào bộ nhớ đệm tại thời điểm cài đặt. Khi service worker xử lý các lần tìm nạp, chúng tôi sẽ kiểm tra xem URL yêu cầu do sự kiện fetch xử lý có nằm trong mảng tài sản được lưu trước đã lưu vào bộ nhớ đệm hay không. Nếu vậy, chúng ta sẽ lấy tài nguyên từ bộ nhớ đệm và bỏ qua mạng. Các yêu cầu khác đi qua mạng, và chỉ mạng. Để xem chiến lược này trong thực tế, xem bản minh hoạ này khi bảng điều khiển đang mở.

Chỉ với mạng

Hiển thị luồng từ trang, đến trình chạy dịch vụ, đến mạng.

Trái ngược với "Chỉ bộ nhớ đệm" là "Chỉ mạng", nơi yêu cầu được chuyển thông qua một trình chạy dịch vụ tới mạng mà không có bất kỳ sự tương tác nào với bộ nhớ đệm của trình chạy dịch vụ. Đây là một chiến lược hay để đảm bảo nội dung mới mẻ (ví dụ như mã đánh dấu), nhưng đổi lại là tính năng này sẽ không bao giờ hoạt động khi người dùng không kết nối mạng.

Việc đảm bảo một yêu cầu đi qua mạng chỉ có nghĩa là bạn không gọi event.respondWith cho một yêu cầu trùng khớp. Nếu bạn muốn thể hiện rõ ràng, bạn có thể chèn một return; trống trong lệnh gọi lại sự kiện fetch cho các yêu cầu mà bạn muốn truyền vào mạng. Đây là những gì xảy ra trong tuỳ chọn "Chỉ bộ nhớ đệm" bản minh hoạ chiến lược cho các yêu cầu không được lưu trước trong bộ nhớ đệm.

Trước tiên, hãy lưu vào bộ nhớ đệm rồi quay lại mạng

Hiển thị luồng từ trang, đến trình chạy dịch vụ, đến bộ nhớ đệm, sau đó đến mạng nếu không có trong bộ nhớ đệm.

Chiến lược này là để mọi thứ trở nên quan trọng hơn một chút. Đối với các yêu cầu so khớp, quy trình này diễn ra như sau:

  1. Yêu cầu truy cập vào bộ nhớ đệm. Nếu nội dung nằm trong bộ nhớ đệm, hãy phân phát nội dung từ đó.
  2. Nếu yêu cầu không có trong bộ nhớ đệm, hãy truy cập vào mạng.
  3. Sau khi hoàn tất yêu cầu mạng, hãy thêm yêu cầu đó vào bộ nhớ đệm thì trả về phản hồi từ mạng.

Sau đây là ví dụ về chiến lược này mà bạn có thể thử nghiệm trong bản minh hoạ trực tiếp:

// Establish a cache name
const cacheName = 'MyFancyCacheName_v1';

self.addEventListener('fetch', (event) => {
  // Check if this is a request for an image
  if (event.request.destination === 'image') {
    event.respondWith(caches.open(cacheName).then((cache) => {
      // Go to the cache first
      return cache.match(event.request.url).then((cachedResponse) => {
        // Return a cached response if we have one
        if (cachedResponse) {
          return cachedResponse;
        }

        // Otherwise, hit the network
        return fetch(event.request).then((fetchedResponse) => {
          // Add the network response to the cache for later visits
          cache.put(event.request, fetchedResponse.clone());

          // Return the network response
          return fetchedResponse;
        });
      });
    }));
  } else {
    return;
  }
});

Mặc dù ví dụ này chỉ đề cập đến hình ảnh, đây là một chiến lược hiệu quả để áp dụng cho tất cả thành phần tĩnh (chẳng hạn như CSS, JavaScript, hình ảnh và phông chữ), đặc biệt là các phiên bản băm. Tính năng này giúp tăng tốc độ cho các tài sản bất biến bằng cách chạy bên cạnh mọi quá trình kiểm tra độ mới của nội dung với máy chủ mà bộ nhớ đệm HTTP có thể bắt đầu. Quan trọng hơn, mọi nội dung được lưu vào bộ nhớ đệm đều sẽ dùng được khi không có mạng.

Ưu tiên kết nối mạng, chuyển lại về bộ nhớ đệm

Hiển thị luồng từ trang, đến trình chạy dịch vụ, đến mạng, sau đó vào bộ nhớ đệm nếu không có mạng.

Nếu bạn định lật "Bộ nhớ đệm trước, giây thứ hai" trên đầu, bạn sẽ kết thúc bằng lời nhắc "Dùng mạng trước, lưu bộ nhớ đệm thứ hai" chiến lược.

  1. Trước tiên, bạn sẽ truy cập vào mạng để xử lý một yêu cầu rồi đặt phản hồi vào bộ nhớ đệm.
  2. Nếu sau này bạn không có kết nối mạng, bạn sẽ quay lại phiên bản mới nhất của phản hồi đó trong bộ nhớ đệm.

Chiến lược này phù hợp cho các yêu cầu HTML hoặc API khi trong khi trực tuyến, bạn muốn có phiên bản mới nhất của tài nguyên, nhưng muốn cấp quyền truy cập ngoại tuyến vào phiên bản có sẵn mới nhất. Khi áp dụng cho các yêu cầu đối với HTML, việc này có thể trông giống như sau:

// Establish a cache name
const cacheName = 'MyFancyCacheName_v1';

self.addEventListener('fetch', (event) => {
  // Check if this is a navigation request
  if (event.request.mode === 'navigate') {
    // Open the cache
    event.respondWith(caches.open(cacheName).then((cache) => {
      // Go to the network first
      return fetch(event.request.url).then((fetchedResponse) => {
        cache.put(event.request, fetchedResponse.clone());

        return fetchedResponse;
      }).catch(() => {
        // If the network is unavailable, get
        return cache.match(event.request.url);
      });
    }));
  } else {
    return;
  }
});

Bạn có thể dùng thử tính năng này ở bản minh hoạ. Trước tiên, hãy truy cập vào trang. Bạn có thể cần tải lại trước khi phản hồi HTML được đặt vào bộ nhớ đệm. Sau đó, trong công cụ cho nhà phát triển, mô phỏng kết nối ngoại tuyến rồi tải lại. Phiên bản mới nhất hiện có sẽ được phân phát ngay từ bộ nhớ đệm.

Trong trường hợp khả năng ngoại tuyến là quan trọng, nhưng bạn cần phải cân bằng giữa khả năng đó với quyền truy cập vào phiên bản mới nhất của mã đánh dấu hoặc dữ liệu API, "Trước tiên, hãy lưu vào bộ nhớ đệm" là một chiến lược vững chắc để đạt được mục tiêu đó.

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

Hiển thị luồng từ trang, trình chạy dịch vụ, đến bộ nhớ đệm, sau đó từ mạng đến bộ nhớ đệm.

Trong số các chiến lược mà chúng tôi đã đề cập cho đến thời điểm này, chiến lược "Đã lỗi thời trong khi xác thực lại" là phức tạp nhất. Nó tương tự như hai chiến lược cuối cùng ở một số khía cạnh, nhưng quy trình này ưu tiên tốc độ truy cập tài nguyên, đồng thời vẫn cập nhật ứng dụng trong nền. Chiến lược này có dạng như sau:

  1. Trong yêu cầu đầu tiên đối với một nội dung, hãy tìm nạp nội dung từ mạng. hãy đặt mã đó vào bộ nhớ đệm rồi trả về phản hồi mạng.
  2. Trong các yêu cầu tiếp theo, phân phát nội dung từ bộ nhớ đệm trước rồi "trong nền" yêu cầu lại từ mạng và cập nhật mục nhập bộ nhớ đệm của nội dung.
  3. Đối với các yêu cầu sau đó, bạn sẽ nhận được phiên bản cuối cùng được tìm nạp từ mạng đã được đặt trong bộ nhớ đệm ở bước trước.

Đây là một chiến lược tuyệt vời cho những điều quan trọng cần luôn cập nhật, nhưng không quan trọng. Hãy nghĩ đến những nội dung như hình đại diện cho một trang mạng xã hội. Chúng được cập nhật khi người dùng thực hiện việc đó, nhưng phiên bản mới nhất không hoàn toàn cần thiết cho mọi yêu cầu.

// Establish a cache name
const cacheName = 'MyFancyCacheName_v1';

self.addEventListener('fetch', (event) => {
  if (event.request.destination === 'image') {
    event.respondWith(caches.open(cacheName).then((cache) => {
      return cache.match(event.request).then((cachedResponse) => {
        const fetchedResponse = fetch(event.request).then((networkResponse) => {
          cache.put(event.request, networkResponse.clone());

          return networkResponse;
        });

        return cachedResponse || fetchedResponse;
      });
    }));
  } else {
    return;
  }
});

Bạn có thể xem ví dụ thực tế trong một bản minh hoạ trực tiếp khác, đặc biệt nếu bạn chú ý đến tab mạng trong công cụ dành cho nhà phát triển trên trình duyệt của mình, và trình xem CacheStorage (nếu công cụ dành cho nhà phát triển trên trình duyệt của bạn có công cụ này).

Chuyển đến Workbox!

Tài liệu này tóm tắt bài đánh giá về API của trình chạy dịch vụ, cũng như các API có liên quan, tức là bạn đã tìm hiểu đủ về cách trực tiếp sử dụng trình chạy dịch vụ để bắt đầu mày mò với Workbox!