Giao tiếp hai chiều với nhân viên dịch vụ

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

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ơ đồ cho thấy một trình chạy dịch vụ và trang trao đổi thông báo.

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ơ đồ minh hoạ hoạt động giao tiếp hai chiều giữa trang và trình chạy dịch vụ, sử dụng Cửa sổ Workbox.

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.
Sơ đồ minh hoạ hoạt động giao tiếp hai chiều giữa trang và trình chạy dịch vụ, cũng như các API trình duyệt có sẵn.

API Broadcast Channel

Hỗ trợ trình duyệt

  • Chrome: 54.
  • Cạnh: 79.
  • Firefox: 38.
  • Safari: 15.4.

Nguồn

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.

Sơ đồ cho thấy hoạt động giao tiếp hai chiều giữa trang và trình chạy dịch vụ, sử dụng đối tượng Broadcast Channel.

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

Hỗ trợ trình duyệt

  • Chrome: 40.
  • Cạnh: 17.
  • Firefox: 44.
  • Safari: 11.1.

Nguồn

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()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'});
  }
});
Sơ đồ cho thấy một trình chạy dịch vụ đang giao tiếp với một mảng ứng dụng.

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

Hỗ trợ trình duyệt

  • Chrome: 2.
  • Cạnh: 12.
  • Firefox: 41.
  • Safari: 5.

Nguồ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
};
Sơ đồ cho thấy một trang truyền cổng đến trình chạy dịch vụ để thiết lập hoạt động giao tiếp hai chiều.

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

Hỗ trợ trình duyệt

  • Chrome: 49.
  • Cạnh: 79.
  • Firefox: không được hỗ trợ.
  • Safari: không được hỗ trợ.

Nguồ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:

Sơ đồ cho thấy một trang truyền cổng đến trình chạy dịch vụ để thiết lập hoạt động giao tiếp hai chiều.

Tìm nạp trong nền

Hỗ trợ trình duyệt

  • Chrome: 74.
  • Cạnh: 79.
  • Firefox: không được hỗ trợ.
  • Safari: không được hỗ trợ.

Nguồ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}%`);
});
Sơ đồ cho thấy một trang truyền cổng đến trình chạy dịch vụ để thiết lập hoạt động giao tiếp hai chiều.
Giao diện người dùng được cập nhật để cho biết tiến trình tải xuống (bên trái). Nhờ có Service worker, hoạt động có thể tiếp tục chạy khi tất cả các thẻ đã đóng (phải).

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ế: