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. Đây có thể được coi 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 việc giao tiếp giữa các ngữ cảnh duyệt web trở nên dễ dàng hơn. Tức là hoạt động giao tiếp giữa các cửa sổ/thẻ, iframe, trình chạy web và trình chạy dịch vụ. Những tin nhắn được đăng lên một kênh cụ thể sẽ được gửi đến tất cả người 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 kênh. Tên này xác định kênh và xuất hiện trong 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 sao chép có cấu trúc hỗ trợ (Chuỗi, Đối tượng, Mảng, Blobs, ArrayBuffer, Map).

Ví dụ – gửi một Blob hoặc tệp

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

Kênh sẽ không phát sóng cho chính kênh đó. 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, sự kiện message đó không kích hoạt.

Điểm 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 điều này có liên quan như thế nào đến các kỹ thuật khác để truyền thông báo, chẳng hạn như WebSockets, SharedWorkers, MessageChannel APIwindow.postMessage(). Broadcast Channel API không thay thế những API này. Mỗi mục đều phục vụ một mục đích. Broadcast Channel API dùng để giao tiếp một với nhiều dễ dàng 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 phát sóng:

  • Phát hiện hành động của người dùng trong các thẻ khác
  • Biết thời điểm người dùng đăng nhập vào 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ố thao tác ở chế độ nền
  • Biết thời điểm một dịch vụ được hoàn tất để thực hiện một số hành động.
  • Khi người dùng tải ảnh lên trong một cửa sổ, hãy chuyển ảnh đó sang các trang đang mở khác.

Ví dụ – trang biết khi nào người dùng đăng xuất, thậm chí từ một thẻ đang mở khác trên cùng 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 trình chạy dịch vụ xoá nội dung được lưu vào bộ nhớ đệm sau khi người dùng thay đổi "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 cách sử dụng window.caches, nhưng trình chạy dịch vụ có thể đã có tiện ích để thực hiện việc này. Chúng ta có thể dùng Broadcast Channel API để hãy 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 từng ứng dụng để có thể giao tiếp từ một trình chạy dịch vụ đến tất cả các ứng dụng khách (mã thực tế thực hiện việc đó). Việc sử dụng Kênh truyền phát sẽ tạo O(1) này 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ộ.

Trong http://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});
    });
    }
};

Mức chênh lệch với postMessage()

Không giống như postMessage(), bạn không còn phải duy trì tệp tham chiếu đến iframe hoặc worker để giao tiếp với iframe đó 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 các nguồn gốc. API Broadcast Channel có cùng nguồn gốc. Vì các tin nhắn được đảm bảo đến từ cùng một nguồn gốc, nên bạn không cần phải xác thực các tin nhắn đó như chúng ta đã từng làm 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ý" cho một kênh cụ thể và có phương thức giao tiếp hai chiều an toàn!

Sự khác biệt với SharedWorkers

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

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

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

Điểm khác biệt chính giữa Channel Messaging APIBroadcastChannel là API này là một phương tiện để gửi thông báo đến nhiều trình nghe (một với nhiều). MessageChannel được dùng để giao tiếp trực tiếp giữa các tập lệnh. Bước này cũng phức tạp hơn, yêu cầu 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!
}

Có một số loại vải polyfill:

Tôi chưa thử những loại xe này, vì vậy quãng đường bạn đã đi có thể thay đổi.

Tài nguyên