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

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

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

Broadcast Channel API เป็น API ง่ายๆ ที่ช่วยให้การติดต่อสื่อสารระหว่างบริบทการท่องเว็บง่ายขึ้น กล่าวคือ การสื่อสารระหว่างหน้าต่าง/แท็บ, iframe, Web Worker และ 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();

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

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

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

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

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

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

ณ จุดนี้ คุณอาจสงสัยว่าเทคนิคนี้เกี่ยวข้องกับเทคนิคอื่นๆ ในการรับส่งข้อความ เช่น WebSocket, SharedWorker, 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 แต่ Service Worker อาจมียูทิลิตีสำหรับดำเนินการนี้อยู่แล้ว เราสามารถใช้ Broadcast Channel API เพื่อนำรหัสนั้นมาใช้ซ้ำได้ หากไม่มี Broadcast Channel API คุณจะต้องวนดูผลลัพธ์ของ self.clients.matchAll() และเรียกใช้ postMessage() ในไคลเอ็นต์แต่ละรายการเพื่อให้การสื่อสารจาก Service Worker ไปยังไคลเอ็นต์ทั้งหมด (โค้ดจริงที่ทําเช่นนั้น) การใช้ช่องสำหรับออกอากาศจะทำให้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()

คุณไม่จำเป็นต้องเก็บรักษาการอ้างอิงถึง iframe หรือ เวิร์กเกอร์เพื่อสื่อสารกับ postMessage() อีกต่อไป

// 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 มีไว้สำหรับการสื่อสารแบบ 1:1 ระหว่างสคริปต์โดยตรง นอกจากนี้ ยังมีความซับซ้อนมากขึ้นเนื่องจากคุณต้องตั้งค่าแชแนลที่มีพอร์ตที่ปลายแต่ละด้าน

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

ปัจจุบัน Chrome 54, Firefox 38 และ Opera 41 รองรับ Broadcast Channel API

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

สำหรับโพลีฟิลล์ มีอยู่ 2-3 รายการดังนี้

เรายังไม่ได้ลองใช้ ระยะไมล์ของคุณอาจต่างจากนี้

แหล่งข้อมูล