Phát thông báo cập nhật đến các trang bằng trình chạy dịch vụ

Andrew Guan
Andrew Guan
Demián Renzulli
Demián Renzulli

Trong một số trường hợp, trình chạy dịch vụ có thể cần chủ động giao tiếp với bất kỳ thẻ đang hoạt động nào mà nó kiểm soát để thông báo về một sự kiện nhất định. Ví dụ:

  • Thông báo cho trang khi một phiên bản mới của trình chạy dịch vụ đã được cài đặt, để trang có thể cho người dùng thấy nút "Cập nhật để làm mới" để truy cập ngay vào chức năng mới.
  • Thông báo cho người dùng về một thay đổi đối với dữ liệu được lưu vào bộ nhớ đệm đã diễn ra ở phía trình chạy dịch vụ, bằng cách hiển thị một chỉ báo, chẳng hạn như: "Ứng dụng hiện đã sẵn sàng hoạt động khi không có mạng" hoặc "Có phiên bản mới của nội dung".
Sơ đồ cho thấy một trình chạy dịch vụ đang giao tiếp với trang để gửi thông tin cập nhật.

Chúng ta sẽ gọi những loại trường hợp sử dụng này là "thông báo truyền tin", trong đó trình chạy dịch vụ không cần nhận thông báo từ trang để bắt đầu giao tiếp. Trong hướng dẫn này, chúng ta sẽ xem xét nhiều cách triển khai loại giao tiếp này giữa các trang và worker dịch vụ bằng cách sử dụng các API trình duyệt tiêu chuẩn và thư viện Workbox.

Trường hợp phát hành công khai

Tinder

Tinder PWA sử dụng workbox-window để lắng nghe các thời điểm quan trọng trong vòng đời của trình chạy dịch vụ trên trang ("đã cài đặt", "được kiểm soát" và "đã kích hoạt"). Bằng cách đó, khi một trình chạy dịch vụ mới hoạt động, biểu ngữ "Có bản cập nhật" sẽ xuất hiện để người dùng có thể làm mới PWA và truy cập vào các tính năng mới nhất:

Ảnh chụp màn hình chức năng "Có bản cập nhật" của ứng dụng web Tinder.
Trong PWA của Tinder, trình chạy dịch vụ sẽ thông báo cho trang rằng phiên bản mới đã sẵn sàng và trang này sẽ cho người dùng thấy biểu ngữ "Có bản cập nhật".

Squoosh

Trong Squoosh PWA, khi trình chạy dịch vụ đã lưu vào bộ nhớ đệm tất cả các thành phần cần thiết để hoạt động khi không có mạng, nó sẽ gửi một thông báo đến trang để hiển thị thông báo ngắn "Sẵn sàng hoạt động khi không có mạng", cho người dùng biết về tính năng này:

Ảnh chụp màn hình chức năng "Sẵn sàng hoạt động khi không có mạng" của ứng dụng web Squoosh.
Trong PWA Squoosh, trình chạy dịch vụ sẽ truyền tin cập nhật đến trang khi bộ nhớ đệm đã sẵn sàng và trang sẽ hiển thị thông báo ngắn "Sẵn sàng hoạt động khi không có mạng".

Sử dụng Workbox

Theo dõi các sự kiện trong vòng đời của trình chạy dịch vụ

workbox-window cung cấp một giao diện đơn giản để theo dõi các sự kiện quan trọng trong vòng đời của trình chạy dịch vụ. Về cơ bản, thư viện này sử dụng các API phía máy khách như updatefoundstatechange, đồng thời cung cấp trình nghe sự kiện cấp cao hơn trong đối tượng workbox-window, giúp người dùng dễ dàng sử dụng các sự kiện này hơn.

Đoạn mã trang sau đây cho phép bạn phát hiện mỗi khi một phiên bản mới của trình chạy dịch vụ được cài đặt, nhờ đó bạn có thể thông báo cho người dùng:

const wb = new Workbox('/sw.js');

wb.addEventListener('installed', (event) => {
  if (event.isUpdate) {
    // Show "Update App" banner
  }
});

wb.register();

Thông báo cho trang về những thay đổi trong dữ liệu bộ nhớ đệm

Gói Workbox workbox-broadcast-update cung cấp một cách tiêu chuẩn để thông báo cho các ứng dụng cửa sổ rằng một phản hồi được lưu vào bộ nhớ đệm đã được cập nhật. Đây là chiến lược thường được dùng nhất cùng với chiến lược StaleWhileRevalidate.

Để truyền tin cập nhật, hãy thêm broadcastUpdate.BroadcastUpdatePlugin vào các lựa chọn chiến lược của bạn ở phía trình chạy dịch vụ:

import {registerRoute} from 'workbox-routing';
import {StaleWhileRevalidate} from 'workbox-strategies';
import {BroadcastUpdatePlugin} from 'workbox-broadcast-update';

registerRoute(
  ({url}) => url.pathname.startsWith('/api/'),
  new StaleWhileRevalidate({
    plugins: [
      new BroadcastUpdatePlugin(),
    ],
  })
);

Trong ứng dụng web, bạn có thể theo dõi các sự kiện này như sau:

navigator.serviceWorker.addEventListener('message', async (event) => {
  // Optional: ensure the message came from workbox-broadcast-update
  if (event.data.meta === 'workbox-broadcast-update') {
    const {cacheName, updatedUrl} = event.data.payload;

    // Do something with cacheName and updatedUrl.
    // For example, get the cached content and update
    // the content on the page.
    const cache = await caches.open(cacheName);
    const updatedResponse = await cache.match(updatedUrl);
    const updatedText = await updatedResponse.text();
  }
});

Sử dụng API trình duyệt

Nếu chức năng mà Workbox cung cấp không đủ cho nhu cầu của bạn, hãy sử dụng các API trình duyệt sau để triển khai "broadcast updates" (cập nhật thông báo):

Broadcast Channel API

Trình chạy dịch vụ tạo một đối tượng BroadcastChannel và bắt đầu gửi thông báo đến đối tượng đó. Mọi ngữ cảnh (ví dụ: trang) muốn nhận các thông báo này đều có thể khởi tạo một đối tượng BroadcastChannel và triển khai một trình xử lý thông báo để nhận thông báo.

Để thông báo cho trang khi một trình chạy dịch vụ mới được cài đặt, hãy sử dụng mã sau:

// Create Broadcast Channel to send messages to the page
const broadcast = new BroadcastChannel('sw-update-channel');

self.addEventListener('install', function (event) {
  // Inform the page every time a new service worker is installed
  broadcast.postMessage({type: 'CRITICAL_SW_UPDATE'});
});

Trang này theo dõi các sự kiện này bằng cách đăng ký sw-update-channel:

// Create Broadcast Channel and listen to messages sent to it
const broadcast = new BroadcastChannel('sw-update-channel');

broadcast.onmessage = (event) => {
  if (event.data && event.data.type === 'CRITICAL_SW_UPDATE') {
    // Show "update to refresh" banner to the user.
  }
};

Đây là một kỹ thuật đơn giản, nhưng hạn chế của kỹ thuật này là khả năng hỗ trợ của trình duyệt: tại thời điểm viết bài này, Safari không hỗ trợ API này.

Client API

Client API cung cấp một cách thức đơn giản để giao tiếp với nhiều ứng dụng từ trình chạy dịch vụ bằng cách lặp lại một mảng các đối tượng Client.

Hãy dùng mã trình chạy dịch vụ sau đây để gửi thông báo đến thẻ được lấy tiêu điểm gần đây nhất:

// 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'});
  }
});

Trang này triển khai một trình xử lý thông báo để chặn các thông báo sau:

// Listen to messages
navigator.serviceWorker.onmessage = (event) => {
     if (event.data && event.data.type === 'MSG_ID') {
         // Process response
   }
};

Client API là một lựa chọn phù hợp cho các trường hợp như phát thông tin đến nhiều thẻ đang hoạt động. API này được tất cả các trình duyệt chính hỗ trợ, nhưng không phải tất cả các phương thức của API này đều được hỗ trợ. Kiểm tra xem trình duyệt có hỗ trợ tính năng này hay không trước khi sử dụng.

Kênh tin nhắn

Message Channel yêu cầu một bước định cấu hình ban đầu, bằng cách truyền một cổng từ trang đến trình chạy dịch vụ, để thiết lập một kênh giao tiếp giữa chúng. Trang này tạo một đối tượng MessageChannel và truyền một cổng đến trình chạy dịch vụ thông qua giao diện postMessage():

const messageChannel = new MessageChannel();

// Init port
navigator.serviceWorker.controller.postMessage({type: 'PORT_INITIALIZATION'}, [
  messageChannel.port2,
]);

Trang này theo dõi các thông báo bằng cách triển khai trình xử lý "onmessage" trên cổng đó:

// Listen to messages
messageChannel.port1.onmessage = (event) => {
  // Process message
};

Trình chạy dịch vụ nhận cổng và lưu thông tin tham chiếu đến cổng đó:

// Initialize
let communicationPort;

self.addEventListener('message', (event) => {
  if (event.data && event.data.type === 'PORT_INITIALIZATION') {
    communicationPort = event.ports[0];
  }
});

Từ đó, trang có thể gửi thông báo bằng cách gọi postMessage() trong phần tham chiếu đến cổng:

// Communicate
communicationPort.postMessage({type: 'MSG_ID' });

MessageChannel có thể phức tạp hơn khi triển khai do cần khởi tạo các cổng, nhưng được tất cả các trình duyệt chính hỗ trợ.

Các bước tiếp theo

Trong hướng dẫn này, chúng ta đã khám phá một trường hợp cụ thể về giao tiếp giữa Cửa sổ và trình chạy dịch vụ: "thông báo nội dung cập nhật". Các ví dụ được khám phá bao gồm việc lắng nghe các sự kiện quan trọng trong sự kiện trong vòng đời của trình chạy dịch vụ và truyền đạt cho trang về những thay đổi trong nội dung hoặc dữ liệu được lưu vào bộ nhớ đệm. Bạn có thể nghĩ đến nhiều trường hợp sử dụng thú vị hơn, trong đó trình chạy dịch vụ chủ động giao tiếp với trang mà không nhận được thông báo nào trước đó.

Để biết thêm các mẫu giao tiếp giữa Cửa sổ và trình chạy dịch vụ, hãy xem:

  • Hướng dẫn về bộ nhớ đệm bắt buộc: Gọi một trình chạy dịch vụ từ trang để lưu trước các tài nguyên vào bộ nhớ đệm (ví dụ: trong các trường hợp tìm nạp trước).
  • Giao tiếp hai chiều: Uỷ quyền một tác vụ cho một trình chạy dịch vụ (ví dụ: một lượt tải xuống lớn) và thông báo cho trang về tiến trình.

Tài nguyên khác