Cho đến nay, mới chỉ 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ả, bạn cần áp dụng một hoặc nhiều chiến lược lưu vào bộ nhớ đệm. Việc này đòi hỏi bạn phải làm quen với giao diện Cache
.
Chiến lược lưu vào bộ nhớ đệm là sự 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 còn tuỳ thuộc vào cách xử lý yêu cầu đối với tài sản tĩnh thay vì tài liệu. Điều này ảnh hưởng đến cách tạo chiến lược lưu vào bộ nhớ đệm.
Trước khi tìm hiểu về các chiến lược, hãy dành chút thời gian để nói về giao diện Cache
có gì, giao diện này là gì và tóm tắt nhanh một số phương thức giao diện 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 chưa từng làm việc với giao diện Cache
, thì bạn có thể nghĩ tương tự như giao diện này hoặc ít nhất là liên quan đến bộ nhớ đệm HTTP. Không phải vậy.
- Giao diện
Cache
là cơ chế lưu vào bộ nhớ đệm hoàn toàn tách biệt với bộ nhớ đệm HTTP. - Việc bạn sử dụng cấu hình
Cache-Control
nào để tác động đến bộ nhớ đệm HTTP sẽ không ảnh hưởng đến những thành phần được lưu trữ trong giao diệnCache
.
Bạn nên coi 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 với các lệnh được thể hiện trong tiêu đề HTTP.
Ngược lại, giao diện Cache
là một 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ác cặp khoá-giá trị HTTP tương đối đơn giản và là một nửa khả năng của các chiến lược lưu vào bộ nhớ đệm.
Dưới đây là 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ụ:
CacheStorage.open
để tạo một thực thểCache
mới.Cache.add
vàCache.put
để lưu trữ phản hồi của mạng trong bộ nhớ đệm của trình chạy dịch vụ.Cache.match
để xác định vị trí một phản hồi đã lưu vào bộ nhớ đệm trong một thực thểCache
.Cache.delete
để xoá phản hồi đã lưu vào bộ nhớ đệm khỏi một thực thểCache
.
Đây chỉ là một vài. Còn có các phương thức hữu ích khác, nhưng đây là những phương thức cơ bản mà bạn sẽ thấy được sử dụng sau này trong hướng dẫn này.
Sự kiện fetch
khiêm tốn
Một nửa còn lại của chiến lược lưu vào bộ nhớ đệm là sự kiện fetch
của trình chạy dịch vụ.
Cho đến thời điểm này trong tài liệu này, bạn đã nghe nói một chút về cách "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 xảy ra điều này:
// 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à ví dụ về đồ chơi (và bạn có thể tự mình xem ví dụ thực tế) – nhưng đây là ví dụ cung cấp thông tin sơ lược về những gì mà worker dịch vụ có thể làm. Mã ở trên thực hiện những việc sau:
- 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. - 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ừ bộ nhớ đệm đó. 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 rồi trả về phản hồi mạng.
- Tất cả các yêu cầu khác được chuyển đi 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 tìm nạp chứa thuộc tính request
. Đây là 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
, là URL cho yêu cầu mạng hiện đang được sự kiệnfetch
xử lý.method
, là phương thức yêu cầu (ví dụ:GET
hoặcPOST
).mode
mô tả chế độ của yêu cầu. Giá trị của'navigate'
thường được dùng để phân biệt các yêu cầu tài liệu HTML với các yêu cầu khác.destination
mô tả loại nội dung đang được yêu cầu theo cách tránh sử dụng đuôi tệp của thành phần được yêu cầu.
Một lần nữa, tính không đồng bộ là tên của trò chơi.
Hãy nhớ rằng sự kiện install
cung cấp phương thức event.waitUntil
đưa ra lời hứa và chờ phương thức đó giải quyết trước khi tiếp tục kích hoạt.
Sự kiện fetch
cung cấp một phương thức event.respondWith
tương tự mà bạn có thể sử dụng để trả về kết quả của yêu cầu fetch
không đồng bộ hoặc phản hồi do phương thức match
của giao diện Cache
trả về.
Chiến lược lưu vào bộ nhớ đệm
Giờ đây, khi đã làm quen với các thực thể Cache
và trình xử lý sự kiện fetch
, bạn có thể tìm hiểu kỹ 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ô tận, nhưng 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 bên trong Workbox.
Chỉ lưu vào bộ nhớ đệm
Hãy bắt đầu với một chiến lược lưu vào bộ nhớ đệm đơn giản mà chúng ta gọi là "Chỉ bộ nhớ đệm". Chỉ là: khi trình chạy dịch vụ có quyền kiểm soát trang, các yêu cầu so khớp sẽ chỉ được chuyển đến bộ nhớ đệm. Điều này có nghĩa là mọi tài sản đã lưu vào bộ nhớ đệm đều phải được lưu trước vào bộ nhớ đệm để mẫu có thể hoạt động. Đồng thời, các tài sả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 loạt tài sản được lưu trước vào bộ nhớ đệm khi cài đặt.
Khi trình chạy dịch vụ xử lý các lượt 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 vào bộ nhớ đệm hay không.
Nếu có, chúng ta lấy tài nguyên từ bộ nhớ đệm và bỏ qua mạng.
Các yêu cầu khác chuyển qua mạng và chỉ chuyển qua mạng.
Để xem cách chiến lược này hoạt động hiệu quả, hãy xem bản minh hoạ này trên bảng điều khiển đang mở.
Chỉ mạng
Trái ngược với "Chỉ bộ nhớ đệm" là "Chỉ mạng", trong đó yêu cầu được chuyển qua trình chạy dịch vụ đến mạng mà không có bất kỳ tương tác nào với bộ nhớ đệm của trình chạy dịch vụ. Đây là chiến lược hay để đảm bảo độ mới của nội dung (hãy nghĩ đến mã đánh dấu), nhưng đổi lại, nó sẽ không bao giờ hoạt động khi người dùng không có kết nối mạng.
Việc đảm bảo một yêu cầu được truyền đến mạng chỉ có nghĩa là bạn không gọi event.respondWith
để có một yêu cầu so khớp.
Nếu muốn nêu rõ, 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 qua mạng.
Đây là điều sẽ xảy ra trong bản minh hoạ chiến lược "Chỉ lưu vào bộ nhớ đệm" đối với các yêu cầu không được lưu trước vào bộ nhớ đệm.
Hãy lưu vào bộ nhớ đệm trước, hãy quay lại mạng
Chiến lược này thúc đẩy mọi thứ tham gia nhiều hơn một chút. Đối với các yêu cầu so khớp, quy trình sẽ diễn ra như sau:
- 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ừ đó.
- Nếu yêu cầu không nằm trong bộ nhớ đệm, hãy kết nối vào mạng.
- Sau khi yêu cầu mạng hoàn tất, hãy thêm yêu cầu đó vào bộ nhớ đệm, rồi trả về phản hồi từ mạng.
Bạn có thể kiểm thử ví dụ sau đây về chiến lược này 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, nhưng đây là một chiến lược tuyệt vời để áp dụng cho tất cả cá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 thành phần có phiên bản hàm băm. Tính năng này giúp tăng tốc độ đối với các nội dung không thể thay đổi bằng cách thực hiện bên cạnh bất kỳ bước kiểm tra độ mới nào 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 đã lưu vào bộ nhớ đệm đều sẽ sử dụng được khi không có mạng.
Trước tiên là mạng, hãy quay lại bộ nhớ đệm
Nếu bạn lật "Bộ nhớ đệm trước tiên, mạng thứ hai" trên đầu, bạn sẽ kết thúc với chiến lược "Mạng trước tiên, bộ nhớ đệm thứ hai", có dạng như sau:
- Trước tiên, bạn truy cập vào mạng để yêu cầu một yêu cầu rồi đặt phản hồi vào bộ nhớ đệm.
- Nếu sau đó 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 với các yêu cầu HTML hoặc API khi bạn muốn sử dụng phiên bản tài nguyên mới nhất, nhưng vẫn muốn cấp quyền truy cập ngoại tuyến vào phiên bản mới nhất có sẵn. Khi áp dụng cho yêu cầu HTML, bạn có thể thấy những thông tin 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 trong bản minh hoạ. Trước tiên, hãy truy cập vào trang. Bạn có thể cần phải 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, hãy mô phỏng kết nối ngoại tuyến rồi tải lại lần nữa. Phiên bản mới nhất có sẵn sẽ được phân phát ngay từ bộ nhớ đệm.
Trong trường hợp khả năng ngoại tuyến đóng vai trò quan trọng, nhưng bạn cần cân bằng chức năng đó với quyền truy cập vào phiên bản mới nhất của một chút dữ liệu đánh dấu hoặc API, "Mạng trước tiên, bộ nhớ đệm thứ hai" 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
Trong số các chiến lược chúng tôi đã đề cập đến nay, chiến lược "Đã lỗi thời trong khi xác thực lại" là chiến lược phức tạp nhất. Theo một cách nào đó, quy trình này tương tự như 2 chiến lược cuối cùng, nhưng quy trình này ưu tiên tốc độ truy cập cho một tài nguyên, trong khi vẫn duy trì cập nhật tài nguyên đó ở chế độ nền. Chiến lược này áp dụng như sau:
- Trong yêu cầu đầu tiên cho một nội dung, hãy tìm nạp nội dung đó từ mạng, đặt nội dung đó vào bộ nhớ đệm rồi trả về phản hồi mạng.
- Trong các yêu cầu tiếp theo, trước tiên, hãy phân phát nội dung từ bộ nhớ đệm, sau đó "ở chế độ nền" rồi yêu cầu lại nội dung từ mạng và cập nhật mục nhập bộ nhớ đệm của tài sản.
- Đối với các yêu cầu sau đó, bạn sẽ nhận được phiên bản gần đây nhất được tìm nạp từ mạng được đặt vào bộ nhớ đệm ở bước trước.
Đây là một chiến lược tuyệt vời cho những thông tin quan trọng cần cập nhật nhưng không quan trọng. Hãy nghĩ đến những thứ như hình đại diện cho trang mạng xã hội. Các API này được cập nhật khi người dùng thực hiện việc đó, nhưng không nhất thiết phải có phiên bản mới nhất trong 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ể thấy tính năng này trong thực tế một bản minh hoạ trực tiếp khác, đặc biệt nếu bạn chú ý đến thẻ mạng trong công cụ cho nhà phát triển của trình duyệt và trình xem CacheStorage
của trình duyệt đó (nếu công cụ cho nhà phát triển của trình duyệt có công cụ như vậy).
Hãy chuyển sang Workbox!
Tài liệu này tóm tắt bài đánh giá của chúng tôi về API của trình chạy dịch vụ, cũng như các API liên quan, có nghĩa 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 sửa đổi với Workbox!