API BroadcastChannel – Một bus thông báo cho web

BroadcastChannel API cho phép các tập lệnh cùng nguồn gốc gửi thông báo đến các ngữ cảnh duyệt web khác. Bạn có thể coi đây là một bus thông báo đơn giản cho phép ngữ nghĩa pub/sub giữa các cửa sổ/thẻ, iframe, trình chạy web và trình chạy dịch vụ.

Kiến thức cơ bản về API

Broadcast Channel API là một API đơn giản giúp giao tiếp giữa các ngữ cảnh duyệt web dễ dàng hơn. Tức là giao tiếp giữa các cửa sổ/thẻ, iframe, worker web và worker dịch vụ. Những tin nhắn được đăng lên một kênh nhất định sẽ được phân phối đến tất cả trình nghe của kênh đó.

Hàm khởi tạo BroadcastChannel nhận một tham số duy nhất: tên của một kênh. Tên này xác định kênh và xuất hiện trong các ngữ cảnh duyệt web.

// Connect to the channel named "my_bus".
const channel = new BroadcastChannel('my_bus');

// Send a message on "my_bus".
channel.postMessage('This is a test message.');

// Listen for messages on "my_bus".
channel.onmessage = function(e) {
    console.log('Received', e.data);
};

// Close the channel when you're done.
channel.close();

Gửi tin nhắn

Thông báo có thể là chuỗi hoặc bất kỳ nội dung nào được thuật toán nhân bản có cấu trúc hỗ trợ (Chuỗi, Đối tượng, Mảng, Blob, ArrayBuffer, Bản đồ).

Ví dụ – gửi Blob hoặc Tệp

channel.postMessage(new Blob(['foo', 'bar'], {type: 'plain/text'}));

Một kênh sẽ không phát sóng đến chính nó. Vì vậy, nếu bạn có trình nghe onmessage trên cùng một trang với postMessage() đến cùng một kênh, thì sự kiện message đó sẽ không kích hoạt.

Sự khác biệt với các kỹ thuật khác

Tại thời điểm này, bạn có thể thắc mắc mối quan hệ giữa phương thức này với các kỹ thuật truyền tin khác như WebSocket, SharedWorker, API MessageChannelwindow.postMessage(). API Broadcast Channel không thay thế các API này. Mỗi loại đều có một mục đích riêng. Broadcast Channel API dùng để dễ dàng giao tiếp một với nhiều giữa các tập lệnh trên cùng một nguồn gốc.

Một số trường hợp sử dụng kênh truyền tin:

  • Phát hiện hành động của người dùng trong các thẻ khác
  • Biết được thời điểm người dùng đăng nhập vào một tài khoản trong một cửa sổ/thẻ khác.
  • Hướng dẫn worker thực hiện một số công việc trong nền
  • Biết được thời điểm một dịch vụ hoàn tất việc thực hiện một số hành động.
  • Khi người dùng tải một bức ảnh lên trong một cửa sổ, hãy chuyển bức ảnh đó đến các trang đang mở khác.

Ví dụ – trang biết thời điểm người dùng đăng xuất, ngay cả khi người dùng đăng xuất trên một thẻ đang mở khác trên cùng một trang web:

<button id="logout">Logout</button>

<script>
function doLogout() {
    // update the UI login state for this page.
}

const authChannel = new BroadcastChannel('auth');

const button = document.querySelector('#logout');
button.addEventListener('click', e => {
    // A channel won't broadcast to itself so we invoke doLogout()
    // manually on this page.
    doLogout();
    authChannel.postMessage({cmd: 'logout', user: 'Eric Bidelman'});
});

authChannel.onmessage = function(e) {
    if (e.data.cmd === 'logout') {
    doLogout();
    }
};
</script>

Trong một ví dụ khác, giả sử bạn muốn hướng dẫn một worker dịch vụ xoá nội dung đã lưu vào bộ nhớ đệm sau khi người dùng thay đổi "chế độ cài đặt bộ nhớ ngoại tuyến" trong ứng dụng của bạn. Bạn có thể xoá bộ nhớ đệm của họ bằng window.caches, nhưng worker dịch vụ có thể đã chứa một tiện ích để thực hiện việc này. Chúng ta có thể sử dụng Broadcast Channel API để sử dụng lại mã đó! Nếu không có Broadcast Channel API, bạn sẽ phải lặp lại kết quả của self.clients.matchAll() và gọi postMessage() trên mỗi ứng dụng để thực hiện việc giao tiếp từ một worker dịch vụ đến tất cả ứng dụng của worker đó (mã thực tế thực hiện việc đó). Việc sử dụng Kênh truyền tin sẽ khiến giá trị này trở thành O(1) thay vì O(N).

Ví dụ – hướng dẫn trình chạy dịch vụ xoá bộ nhớ đệm, sử dụng lại các phương thức tiện ích nội bộ của trình chạy dịch vụ.

Trong index.html

const channel = new BroadcastChannel('app-channel');
channel.onmessage = function(e) {
    if (e.data.action === 'clearcache') {
    console.log('Cache removed:', e.data.removed);
    }
};

const messageChannel = new MessageChannel();

// Send the service worker a message to clear the cache.
// We can't use a BroadcastChannel for this because the
// service worker may need to be woken up. MessageChannels do that.
navigator.serviceWorker.controller.postMessage({
    action: 'clearcache',
    cacheName: 'v1-cache'
}, [messageChannel.port2]);

Trong sw.js

function nukeCache(cacheName) {
    return caches.delete(cacheName).then(removed => {
    // ...do more stuff (internal) to this service worker...
    return removed;
    });
}

self.onmessage = function(e) {
    const action = e.data.action;
    const cacheName = e.data.cacheName;

    if (action === 'clearcache') {
    nukeCache(cacheName).then(removed => {
        // Send the main page a response via the BroadcastChannel API.
        // We could also use e.ports[0].postMessage(), but the benefit
        // of responding with the BroadcastChannel API is that other
        // subscribers may be listening.
        const channel = new BroadcastChannel('app-channel');
        channel.postMessage({action, removed});
    });
    }
};

Sự khác biệt với postMessage()

Không giống như postMessage(), bạn không cần duy trì tham chiếu đến iframe hoặc worker để giao tiếp với iframe hoặc worker đó nữa:

// Don't have to save references to window objects.
const popup = window.open('https://another-origin.com', ...);
popup.postMessage('Sup popup!', 'https://another-origin.com');

window.postMessage() cũng cho phép bạn giao tiếp trên nhiều nguồn gốc. Broadcast Channel API là cùng nguồn gốc. Vì các thông báo được đảm bảo là đến từ cùng một nguồn gốc, nên bạn không cần xác thực các thông báo đó như trước đây với window.postMessage():

// Don't have to validate the origin of a message.
const iframe = document.querySelector('iframe');
iframe.contentWindow.onmessage = function(e) {
    if (e.origin !== 'https://expected-origin.com') {
    return;
    }
    e.source.postMessage('Ack!', e.origin);
};

Bạn chỉ cần "đăng ký" theo dõi một kênh cụ thể và có thể giao tiếp hai chiều một cách an toàn!

Điểm khác biệt với SharedWorkers

Sử dụng BroadcastChannel cho các trường hợp đơn giản mà bạn cần gửi thông báo đến một số cửa sổ/thẻ hoặc worker.

Đối với các trường hợp sử dụng phức tạp hơn như quản lý khoá, trạng thái dùng chung, đồng bộ hoá tài nguyên giữa máy chủ và nhiều ứng dụng khách hoặc chia sẻ kết nối WebSocket với máy chủ từ xa, worker dùng chung là giải pháp phù hợp nhất.

Sự khác biệt với API MessageChannel

Điểm khác biệt chính giữa Channel Messaging APIBroadcastChannel là API sau là một phương tiện để gửi thông báo đến nhiều trình nghe (một đến nhiều). MessageChannel dùng để giao tiếp trực tiếp giữa các tập lệnh. Phương thức này cũng phức tạp hơn, đòi hỏi bạn phải thiết lập các kênh có một cổng ở mỗi đầu.

Phát hiện tính năng và hỗ trợ trình duyệt

Hiện tại, Chrome 54, Firefox 38 và Opera 41 hỗ trợ Broadcast Channel API.

if ('BroadcastChannel' in self) {
    // BroadcastChannel API supported!
}

Đối với polyfill, có một số polyfill sau:

Tôi chưa thử những cách này nên kết quả của bạn có thể khác.

Tài nguyên