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

BroadcastChannel API מאפשר לסקריפטים מאותו מקור לשלוח הודעות להקשרים אחרים של גלישה. אפשר לחשוב עליו כעל אוטובוס הודעות פשוט שמאפשר סמנטיקה של פרסום/הרשמה (pub/sub) בין חלונות/כרטיסיות, רכיבי iframe, מכונות worker באינטרנט וקובצי שירות.

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

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

ה-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(). ממשק ה-API של ערוץ השידור לא מחליף את ממשקי ה-API האלה. לכל אחת מהן יש מטרה. Broadcast Channel 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>

דוגמה נוספת: נניח שרציתם להורות לקובץ שירות להסיר תוכן שנשמר במטמון אחרי שהמשתמש משנה את 'הגדרת האחסון במצב אופליין' באפליקציה. תוכלו למחוק את המטמון שלהם באמצעות window.caches, אבל יכול להיות שכבר יש בקובץ השירות כלי שעוזר לעשות את זה. אנחנו יכולים להשתמש ב-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 הוא ממשק 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 הוא שהאחרון משמש לשליחת הודעות למספר מאזינים (אחד-לרבים). MessageChannel מיועד לתקשורת חד-ל-חד בין סקריפטים. בנוסף, התהליך מורכב יותר, וצריך להגדיר ערוצים עם יציאה בכל קצה.

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

נכון לעכשיו, יש תמיכה ב-Broadcast Channel API בגרסאות Chrome 54,‏ Firefox 38 ו-Opera 41.

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

לגבי polyfills, יש כמה:

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

משאבים