BroadcastChannel API - รถบัสข้อความสำหรับเว็บ

BroadcastChannel API ช่วยให้สคริปต์ต้นทางเดียวกันส่งข้อความถึงบริบทการท่องเว็บอื่นๆ ได้ อาจมองว่าเป็นบัสข้อความแบบง่ายๆ ที่อนุญาตให้ใช้ความหมายของ pub/sub ระหว่างหน้าต่าง/แท็บ, iframe, Web Worker และ Service Worker

ข้อมูลเบื้องต้นเกี่ยวกับ API

Broadcast Channel API เป็น API แบบง่ายที่ช่วยให้การสื่อสารระหว่างบริบทการท่องเว็บเป็นไปอย่างง่ายดายยิ่งขึ้น กล่าวคือ การสื่อสารระหว่างหน้าต่าง/แท็บ, iframe, โปรแกรมทำงานของบริการ และ Service Worker ข้อความที่โพสต์ในช่องใดช่องหนึ่งจะส่งไปยังผู้ฟังทุกคนของช่องนั้น

ตัวสร้าง BroadcastChannel จะมีพารามิเตอร์เดียว นั่นคือชื่อแชแนล ชื่อจะระบุช่องและปรากฏอยู่ในบริบทต่างๆ ของการท่องเว็บ

// 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();

การส่งข้อความ

ข้อความอาจเป็นสตริงหรือสิ่งใดก็ตามที่อัลกอริทึมโคลนที่มีโครงสร้าง (สตริง, ออบเจ็กต์, อาร์เรย์, Blobs, ArrayBuffer, Map) ที่รองรับ

ตัวอย่าง - การส่ง Blob หรือไฟล์

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

ช่องจะไม่ประกาศไปยังช่องนั้น ดังนั้นหากคุณมี Listener onmessage ในหน้าเดียวกับ postMessage() ไปยังช่องเดียวกัน เหตุการณ์ message นั้นจะไม่เริ่มทำงาน

ความแตกต่างกับเทคนิคอื่นๆ

ณ จุดนี้ คุณอาจสงสัยว่ากรณีนี้เกี่ยวข้องกับเทคนิคอื่นๆ ในการส่งผ่านข้อความ เช่น WebSockets, SharedWorkers, MessageChannel API และ window.postMessage() อย่างไร Broadcast Channel API ไม่ได้มาแทนที่ API เหล่านี้ แต่ละองค์ประกอบมีไว้สำหรับวัตถุประสงค์ Broadcast Channel API มีไว้เพื่อการสื่อสารแบบ 1 ต่อหลายคนได้ง่ายระหว่างสคริปต์ในต้นทางเดียวกัน

ตัวอย่างการใช้งานช่องออกอากาศมีดังนี้

  • ตรวจหาการดำเนินการของผู้ใช้ในแท็บอื่นๆ
  • ทราบเมื่อผู้ใช้เข้าสู่ระบบบัญชีในหน้าต่าง/แท็บอื่น
  • สั่งให้ผู้ปฏิบัติงานทำงานเบื้องหลังบางอย่าง
  • ทราบเมื่อบริการทำงานบางอย่างเสร็จสิ้น
  • เมื่อผู้ใช้อัปโหลดรูปภาพในหน้าต่างหนึ่ง ให้ส่งต่อรูปภาพดังกล่าวไปยังหน้าอื่นๆ ที่เปิดอยู่

ตัวอย่าง - หน้าเว็บที่ทราบเมื่อผู้ใช้ออกจากระบบ แม้ว่าจะมาจากแท็บอื่นที่เปิดอยู่ในเว็บไซต์เดียวกัน เช่น

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

ในอีกตัวอย่างหนึ่ง สมมติว่าคุณต้องการสั่งให้ Service Worker นำเนื้อหาที่แคชไว้ออกหลังจากที่ผู้ใช้เปลี่ยน "การตั้งค่าพื้นที่เก็บข้อมูลออฟไลน์" ในแอปของคุณ คุณสามารถลบแคชของผู้ใช้ได้โดยใช้ window.caches แต่โปรแกรมทำงานของบริการอาจมียูทิลิตี้สำหรับการดำเนินการดังกล่าวอยู่แล้ว เราสามารถใช้ Broadcast Channel API เพื่อนำโค้ดนั้น มาใช้ซ้ำได้! หากไม่มี Broadcast Channel API คุณจะต้องวนผลลัพธ์ของ self.clients.matchAll() และเรียกใช้ postMessage() บนไคลเอ็นต์แต่ละรายเพื่อให้สื่อสารจากโปรแกรมทำงานของบริการไปยังไคลเอ็นต์ทั้งหมดของลูกค้าได้ (โค้ดจริงที่ทำเช่นนั้น) การใช้ช่องออกอากาศจะทำให้ช่องนี้เป็น O(1) แทน O(N)

ตัวอย่าง - สั่งให้ Service Worker นำแคชออกโดยนำวิธีการยูทิลิตีภายในกลับมาใช้ซ้ำ

ใน 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]);

ใน 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});
    });
    }
};

ความแตกต่างกับ postMessage()

สิ่งที่แตกต่างจาก postMessage() คือคุณไม่จำเป็นต้องดูแลรักษาการอ้างอิงไปยัง iframe หรือผู้ปฏิบัติงานเพื่อติดต่อสื่อสารกับ iframe อีกต่อไป ดังนี้

// 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() ยังช่วยให้คุณสื่อสารข้ามต้นทางได้ด้วย Broadcast Channel API มาจากแหล่งเดียวกัน เนื่องจากข้อความมีการรับประกันว่ามาจากต้นทางเดียวกัน จึงไม่มีความจำเป็นที่จะต้องตรวจสอบอีเมลเหมือนที่เคยทำกับ 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);
};

เพียง "ติดตาม" ช่องที่ต้องการและสื่อสารแบบ 2 ทิศทางได้อย่างปลอดภัย

ความแตกต่างกับ SharedWorker

ใช้ BroadcastChannel สำหรับกรณีง่ายๆ ที่คุณต้องส่งข้อความไปยังหน้าต่าง/แท็บ หรือผู้ปฏิบัติงานหลายรายการ

สำหรับกรณีการใช้งานที่แปลกใหม่กว่า เช่น การจัดการล็อก สถานะที่แชร์ การซิงค์ทรัพยากรระหว่างเซิร์ฟเวอร์กับไคลเอ็นต์หลายเครื่อง หรือการแชร์การเชื่อมต่อ WebSocket กับโฮสต์ระยะไกล ผู้ปฏิบัติงานที่ใช้งานร่วมกันคือโซลูชันที่เหมาะสมที่สุด

ความแตกต่างกับ MessageChannel API

ความแตกต่างที่สำคัญระหว่าง Channel Messaging API กับ BroadcastChannel คือวิธีแบบหลังเป็นวิธีที่จะส่งข้อความถึงผู้ฟังหลายราย (1 ต่อหลายคน) MessageChannel มีไว้เพื่อการสื่อสารแบบหนึ่งต่อหนึ่งโดยตรงระหว่างสคริปต์ ทั้งยังมีความซับซ้อนมากขึ้น ทำให้คุณต้องตั้งค่าช่องโดยใช้พอร์ตที่แต่ละฝั่ง

การตรวจหาฟีเจอร์และการรองรับเบราว์เซอร์

ในปัจจุบัน Chrome 54, Firefox 38 และ Opera 41 สนับสนุน Broadcast Channel API

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

โพลีฟิลมี 2-3 รายการให้บริการ ดังนี้

ยังไม่เคยลองวิธีนี้ ระยะทางอาจแตกต่างออกไป

แหล่งข้อมูล