BroadcastChannel API – אפיק הודעות לאינטרנט

BroadcastChannel API מאפשר לסקריפטים מאותו מקור לשלוח הודעות להקשרי גלישה אחרים. אפשר להתייחס אליו כאל אפיק הודעות פשוט שמאפשר סמנטיקה של Pub/Sub בין חלונות/כרטיסיות, מסגרות iframe, עובדי אינטרנט ו-service worker.

עקרונות בסיסיים של API

Broadcast Channel API הוא ממשק API פשוט שמאפשר תקשורת בין ההקשרים השונים של הגלישה. כלומר, תקשורת בין חלונות/כרטיסיות, מסגרות iframe, קובצי עבודה באינטרנט ו-Service Workers. הודעות שמתפרסמות בערוץ מסוים נשלחות לכל המאזינים של הערוץ.

ה-constructor של 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, מפה).

דוגמה – שליחת Blob או קובץ

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

הערוץ לא ישודר לעצמו. אז אם יש לך האזנה ל-onmessage באותו דף כמו postMessage() לאותו ערוץ, האירוע message לא מופעלת.

הבדלים לעומת שיטות אחרות

בשלב הזה, ייתכן שתהיתם איך זה קשור לשיטות אחרות להעברת הודעות, כמו WebSockets , SharedWorkers, API של MessageChannel ו-window.postMessage(). Broadcast Channel API לא מחליף את ממשקי ה-API האלה. כל אחת מהן משרתת מטרה. ממשק ה-API של ערוץ השידור נועד לתקשורת פשוטה של אחד לרבים בין סקריפטים מאותו מקור.

מספר תרחישים לדוגמה של ערוצי שידור:

  • זיהוי פעולות של משתמשים בכרטיסיות אחרות
  • קבלת עדכון כשמשתמש מתחבר לחשבון בחלון אחר או בכרטיסייה אחרת.
  • להנחות עובד לבצע עבודות ברקע
  • לדעת מתי שירות מבצע פעולה כלשהי.
  • כשהמשתמש מעלה תמונה בחלון אחד, מעבירים אותה לדפים פתוחים אחרים.

דוגמה – דף שיודע מתי המשתמש מתנתק, גם מכרטיסייה פתוחה אחרת באותו אתר:

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

בשונה מ-postMessage(), כבר לא צריך לתחזק הפניה ל-iframe או ב-worker כדי לתקשר איתם:

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

פשוט "הרשמה" לערוץ מסוים ולקבל תקשורת דו-כיוונית מאובטחת!

ההבדל בין SharedWorkers

כדאי להשתמש ב-BroadcastChannel במקרים פשוטים שבהם צריך לשלוח הודעה לכמה חלונות/כרטיסיות, או לעובדים באופן פוטנציאלי.

הפתרון המתאים ביותר הוא ניהול מנעולים, מצב משותף, סנכרון משאבים בין שרת למספר לקוחות או שיתוף חיבור WebSocket עם מארח מרוחק.

ההבדל ב-MessageChannel API

ההבדל העיקרי בין Channel Messaging API לבין BroadcastChannel הוא שה-API הזה הוא אמצעי לשליחת הודעות למספר מאזינים (אחד לרבים). MessageChannel מיועד לתקשורת אחד על אחד ישירות בין סקריפטים. לשם כך, התהליך מורכב יותר, וצריך להגדיר ערוצים עם יציאה בכל קצה.

זיהוי תכונות ותמיכה בדפדפן

נכון לעכשיו, Chrome 54, Firefox 38 ו-Opera 41 תומכים בממשק ה-API של ערוץ השידור.

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

יש כמה דוגמאות ל-polyfills:

לא ניסיתי את האפשרויות האלה, ולכן יכול להיות שמרחק הנסיעה המצטבר יהיה שונה.

משאבים