Trong một số trường hợp, ứng dụng web có thể cần thiết lập kênh liên lạc hai chiều giữa và trình chạy dịch vụ.
Ví dụ: Trong PWA podcast, người dùng có thể tạo một tính năng để cho phép người dùng tải các tập xuống tiêu thụ ngoại tuyến và cho phép trình chạy dịch vụ cập nhật trang thường xuyên được thông báo về tiến trình, vì vậy, luồng có thể cập nhật giao diện người dùng.
Trong hướng dẫn này, chúng ta sẽ khám phá các cách khác nhau để triển khai liên lạc hai chiều giữa Window (Cửa sổ) và service (dịch vụ) ngữ cảnh worker, bằng cách khám phá API khác nhau, thư viện Hộp công việc, cũng như một số trường hợp nâng cao.
Sử dụng Workbox
workbox-window
là một tập hợp
các mô-đun của thư viện Hộp công việc được thiết kế
để chạy trong ngữ cảnh cửa sổ. Workbox
Lớp cung cấp phương thức messageSW()
để gửi thông báo đến trình chạy dịch vụ đã đăng ký của thực thể và
đang chờ phản hồi.
Đoạn mã trang sau đây sẽ tạo một phiên bản Workbox
mới và gửi thông báo cho trình chạy dịch vụ
để có phiên bản tương ứng:
const wb = new Workbox('/sw.js');
wb.register();
const swVersion = await wb.messageSW({type: 'GET_VERSION'});
console.log('Service Worker version:', swVersion);
Service worker triển khai trình nghe thông báo ở đầu bên kia và phản hồi trình xử lý đã đăng ký trình chạy dịch vụ:
const SW_VERSION = '1.0.0';
self.addEventListener('message', (event) => {
if (event.data.type === 'GET_VERSION') {
event.ports[0].postMessage(SW_VERSION);
}
});
Về cơ bản, thư viện sử dụng API trình duyệt mà chúng ta sẽ xem xét trong phần tiếp theo: Thông báo Kênh, nhưng rút gọn nhiều chi tiết triển khai, giúp sử dụng dễ dàng hơn, trong khi tận dụng trình duyệt rộng API này có.
Sử dụng API trình duyệt
Nếu thư viện Workbox không đủ cho nhu cầu của bạn, bạn có thể dùng một số API cấp thấp hơn để triển khai hoạt động giao tiếp "hai chiều" giữa các trang và trình chạy dịch vụ. Chúng có một số điểm tương đồng và điểm khác biệt:
Điểm tương đồng:
- Trong mọi trường hợp, hoạt động giao tiếp bắt đầu ở một đầu qua giao diện
postMessage()
và được nhận bằng cách triển khai trình xử lýmessage
. - Trên thực tế, tất cả API hiện có đều cho phép chúng ta triển khai cùng một trường hợp sử dụng, nhưng có một vài API có thể đơn giản hoá quá trình phát triển trong một số trường hợp.
Điểm khác biệt:
- Họ có nhiều cách khác nhau để xác định mặt kia của giao tiếp: một vài người trong số họ sử dụng tham chiếu rõ ràng đến ngữ cảnh khác, trong khi những ngữ cảnh khác có thể giao tiếp ngầm qua proxy đối tượng được tạo thực thể ở mỗi bên.
- Khả năng hỗ trợ trình duyệt sẽ khác nhau giữa các trình duyệt.
API Broadcast Channel
API Kênh phát sóng cho phép giao tiếp cơ bản giữa các ngữ cảnh duyệt qua qua broadcastChannel .
Để triển khai, trước tiên, mỗi ngữ cảnh phải tạo thực thể cho đối tượng BroadcastChannel
bằng cùng một mã nhận dạng
và gửi và nhận tin nhắn từ đó:
const broadcast = new BroadcastChannel('channel-123');
Đối tượng BroadcastChannel hiển thị giao diện postMessage()
để gửi thông báo cho bất kỳ lắng nghe nào
ngữ cảnh:
//send message
broadcast.postMessage({ type: 'MSG_ID', });
Mọi ngữ cảnh của trình duyệt đều có thể nghe tin nhắn thông qua phương thức onmessage
của BroadcastChannel
đối tượng:
//listen to messages
broadcast.onmessage = (event) => {
if (event.data && event.data.type === 'MSG_ID') {
//process message...
}
};
Như đã thấy, không có sự tham chiếu rõ ràng đến một ngữ cảnh cụ thể, do đó không cần phải có được tham chiếu đến trình chạy dịch vụ hoặc bất kỳ ứng dụng cụ thể nào.
Nhược điểm là tại thời điểm viết bài này, API có sự hỗ trợ của Chrome, Firefox và Edge, nhưng các trình duyệt khác như Safari, không hỗ trợ .
API ứng dụng
API Ứng dụng cho phép bạn có được
tham chiếu đến mọi đối tượng WindowClient
đại diện cho các thẻ đang hoạt động mà trình chạy dịch vụ đang kiểm soát.
Vì trang do một trình chạy dịch vụ kiểm soát, nên trang sẽ nghe và gửi thông báo đến
trình chạy dịch vụ đang hoạt động trực tiếp qua giao diện serviceWorker
:
//send message
navigator.serviceWorker.controller.postMessage({
type: 'MSG_ID',
});
//listen to messages
navigator.serviceWorker.onmessage = (event) => {
if (event.data && event.data.type === 'MSG_ID') {
//process response
}
};
Tương tự, trình chạy dịch vụ cũng theo dõi thông báo bằng cách triển khai trình nghe onmessage
:
//listen to messages
self.addEventListener('message', (event) => {
if (event.data && event.data.type === 'MSG_ID') {
//Process message
}
});
Để giao tiếp lại với bất kỳ ứng dụng nào, trình chạy dịch vụ sẽ nhận được một mảng
Đối tượng WindowClient
bằng cách thực thi
các phương thức như
Clients.matchAll()
và
Clients.get()
. Sau đó, Google Analytics có thể
postMessage()
bất kỳ giá trị nào:
//Obtain an array of Window client objects
self.clients.matchAll(options).then(function (clients) {
if (clients && clients.length) {
//Respond to last focused tab
clients[0].postMessage({type: 'MSG_ID'});
}
});
Client API
là một lựa chọn thích hợp để dễ dàng giao tiếp với tất cả thẻ đang hoạt động của một trình chạy dịch vụ
theo cách tương đối đơn giản. API này được tất cả các kênh
trình duyệt,
nhưng không phải phương thức nào cũng có sẵn, vì vậy, hãy nhớ kiểm tra xem trình duyệt có hỗ trợ phương thức đó trước
triển khai nó trong trang web của bạn.
Kênh tin nhắn
Cần có Kênh tin nhắn xác định và truyền một cổng từ ngữ cảnh này sang ngữ cảnh khác để thiết lập hoạt động giao tiếp hai chiều của bạn.
Để khởi tạo kênh, trang sẽ tạo thực thể cho đối tượng MessageChannel
và sử dụng đối tượng đó
để gửi một cổng cho trình chạy dịch vụ đã đăng ký. Trang này cũng triển khai trình nghe onmessage
trên
nó để nhận thông báo từ ngữ cảnh khác:
const messageChannel = new MessageChannel();
//Init port
navigator.serviceWorker.controller.postMessage({type: 'PORT_INITIALIZATION'}, [
messageChannel.port2,
]);
//Listen to messages
messageChannel.port1.onmessage = (event) => {
// Process message
};
Service worker nhận cổng, lưu tham chiếu đến và sử dụng nó để gửi thông báo cho bên cạnh:
let communicationPort;
//Save reference to port
self.addEventListener('message', (event) => {
if (event.data && event.data.type === 'PORT_INITIALIZATION') {
communicationPort = event.ports[0];
}
});
//Send messages
communicationPort.postMessage({type: 'MSG_ID'});
MessageChannel
hiện được tất cả các kênh hỗ trợ
trình duyệt.
API nâng cao: Đồng bộ hoá trong nền và Tìm nạp trong nền
Trong hướng dẫn này, chúng tôi đã khám phá những cách triển khai các kỹ thuật giao tiếp hai chiều, đối với các trường hợp đơn giản, chẳng hạn như truyền một thông báo chuỗi mô tả thao tác sẽ thực hiện hoặc danh sách URL để lưu vào bộ nhớ đệm từ ngữ cảnh này sang ngữ cảnh khác. Trong phần này, chúng ta sẽ khám phá 2 API để xử lý các trường hợp: thiếu kết nối và thời gian tải xuống lâu.
Đồng bộ hoá ở chế độ nền
Một ứng dụng trò chuyện có thể muốn đảm bảo rằng tin nhắn không bao giờ bị mất do kết nối kém. Chiến lược phát hành đĩa đơn API đồng bộ hoá trong nền cho phép bạn trì hoãn các hành động được thử lại khi người dùng có kết nối ổn định. Điều này rất hữu ích để đảm bảo rằng bất kỳ nội dung nào người dùng muốn gửi, đều thực sự được gửi.
Thay vì giao diện postMessage()
, trang này sẽ đăng ký một sync
:
navigator.serviceWorker.ready.then(function (swRegistration) {
return swRegistration.sync.register('myFirstSync');
});
Sau đó, trình chạy dịch vụ sẽ theo dõi sự kiện sync
để xử lý thông báo:
self.addEventListener('sync', function (event) {
if (event.tag == 'myFirstSync') {
event.waitUntil(doSomeStuff());
}
});
Hàm doSomeStuff()
phải trả về một lời hứa cho biết thành công/thất bại của bất kỳ nội dung nào
cố gắng thực hiện. Nếu quá trình này diễn ra thì quá trình đồng bộ hoá đã hoàn tất. Nếu không thành công, một lần đồng bộ hoá khác sẽ được lên lịch để
hãy thử lại. Thử đồng bộ hoá lại cũng chờ kết nối và sử dụng thuật toán thời gian đợi luỹ thừa.
Sau khi thao tác đã được thực hiện, trình chạy dịch vụ có thể liên lạc lại với trang để cập nhật giao diện người dùng bằng cách sử dụng bất kỳ API giao tiếp nào được khám phá trước đó.
Google Tìm kiếm sử dụng tính năng Đồng bộ hóa dưới nền để duy trì các truy vấn không thành công do kết nối kém và thử lại chúng sau này khi người dùng trực tuyến. Sau khi thực hiện thao tác, các dịch vụ sẽ thông báo kết quả cho người dùng thông qua thông báo đẩy trên web:
Tìm nạp trong nền
Đối với các công việc tương đối ngắn như gửi thư hoặc danh sách URL để lưu vào bộ nhớ đệm, các tùy chọn khám phá từ trước đến nay là một lựa chọn tốt. Nếu tác vụ mất quá nhiều thời gian, trình duyệt sẽ tắt dịch vụ nếu không sẽ gây rủi ro cho quyền riêng tư và pin của người dùng.
API tìm nạp trong nền cho phép bạn giảm tải một nhiệm vụ cần nhiều thời gian cho một trình chạy dịch vụ, chẳng hạn như tải phim, podcast hoặc cấp độ xuống của một trò chơi.
Để giao tiếp với trình chạy dịch vụ từ trang, hãy sử dụng backgroundFetch.fetch
, thay vì
postMessage()
:
navigator.serviceWorker.ready.then(async (swReg) => {
const bgFetch = await swReg.backgroundFetch.fetch(
'my-fetch',
['/ep-5.mp3', 'ep-5-artwork.jpg'],
{
title: 'Episode 5: Interesting things.',
icons: [
{
sizes: '300x300',
src: '/ep-5-icon.png',
type: 'image/png',
},
],
downloadTotal: 60 * 1024 * 1024,
},
);
});
Đối tượng BackgroundFetchRegistration
cho phép trang theo dõi sự kiện progress
tiến trình tải xuống:
bgFetch.addEventListener('progress', () => {
// If we didn't provide a total, we can't provide a %.
if (!bgFetch.downloadTotal) return;
const percent = Math.round(
(bgFetch.downloaded / bgFetch.downloadTotal) * 100,
);
console.log(`Download progress: ${percent}%`);
});
Các bước tiếp theo
Trong hướng dẫn này, chúng ta đã tìm hiểu trường hợp giao tiếp chung nhất giữa trình chạy trang và trình chạy dịch vụ (giao tiếp hai chiều).
Trong nhiều trường hợp, một người có thể chỉ cần một bối cảnh để giao tiếp với nhau mà không cần phải nhận của bạn. Hãy xem các hướng dẫn sau để biết cách triển khai các kỹ thuật một chiều trong các trang của bạn từ và đến trình chạy dịch vụ, cùng với các trường hợp sử dụng và ví dụ hoạt động trong thực tế:
- Hướng dẫn lưu vào bộ nhớ đệm ngầm định: Gọi một trình chạy dịch vụ từ trang đến bộ nhớ đệm trước (ví dụ: trong các trường hợp tìm nạp trước).
- Cập nhật thông tin truyền phát: Gọi trang từ trình chạy dịch vụ để thông báo về các bản cập nhật quan trọng (ví dụ: có phiên bản mới của ứng dụng web).