De temps en temps, les développeurs doivent établir une communication entre plusieurs onglets de navigateur pour pouvoir envoyer des messages d'un onglet à un autre et recevoir des réponses. Nous avons également fait face à ce besoin à un moment donné.
Certaines solutions existent dĂ©jĂ (comme, par exemple, l'API BroadcastChannel). Cependant, la prise en charge de son navigateur laisse beaucoup Ă dĂ©sirer , nous avons donc dĂ©cidĂ© d'utiliser notre propre bibliothĂšque. Lorsque la bibliothĂšque Ă©tait prĂȘte, cette fonctionnalitĂ© n'Ă©tait plus requise. NĂ©anmoins, une autre tĂąche a Ă©mergĂ©: la communication entre un iframe et la fenĂȘtre principale.
En y regardant de plus prĂšs, il s'est avĂ©rĂ© que les deux tiers de la bibliothĂšque ne devraient pas ĂȘtre modifiĂ©s - seule une refactorisation de code Ă©tait nĂ©cessaire. La bibliothĂšque est un PROTOCOLE de communication qui peut fonctionner avec des donnĂ©es texte. Il peut ĂȘtre appliquĂ© dans tous les cas dans lesquels le texte est transfĂ©rĂ©, comme les iframes, window.open, travailleur, onglets du navigateur ou WebSocket.
Comment ça marche
Actuellement, le protocole a deux fonctions: l'envoi de messages et l'abonnement aux événements. Tout message du protocole est un objet de données. Pour nous, le champ principal de cet objet est type , qui nous indique de quel type de message il s'agit. Le champ type est une énumération avec les valeurs suivantes:
- 0 - envoyer un message
- 1 - envoyer une demande
- 2 - recevoir une réponse.
Envoi d'un message
L'envoi d'un message n'implique pas de réponse. Pour envoyer un message, nous créons un objet avec les champs suivants:
- type - type d' événement 0
- name - nom de l'événement utilisateur
- data - données utilisateur (de type JSON).
Lors de la réception d'un message de l'autre cÎté avec le champ type = 0 , nous savons qu'il s'agit d'un événement, avec un nom et des données d'événement existants. Tout ce que nous avons à faire est de diffuser l'événement (presque un modÚle EventEmitter standard).
Comment cela fonctionne dans un schéma simple:

Envoi d'une demande
L'envoi d'une demande implique qu'au sein de la bibliothĂšque, un identifiant de demande est créé et la bibliothĂšque attendra une rĂ©ponse avec l' identifiant . Une fois la rĂ©ponse reçue avec succĂšs, tous les champs auxiliaires en seront supprimĂ©s et la rĂ©ponse sera renvoyĂ©e Ă l'utilisateur. De plus, le temps de rĂ©ponse maximum peut ĂȘtre rĂ©glĂ©.

Quant aux demandes, c'est un peu plus compliqué. Pour répondre à une demande, vous devez annoncer les méthodes disponibles dans notre protocole. Cela se fait avec la méthode registerRequestHandler . Il accepte le nom d'une demande de réponse et une fonction qui renvoie la réponse. Pour créer une demande, nous avons besoin d'un identifiant et nous pouvons essentiellement utiliser l' horodatage , mais il n'est pas trÚs pratique de l'ajuster. Il s'agit donc d'un ID de classe qui envoie une réponse + un numéro de réponse + un littéral de chaßne. Nous créons maintenant un objet avec les champs suivants: id , type = 1 , nom comme nom de demande et données comme données utilisateur (de type JSON).
à la réception d'une demande, nous vérifions si nous avons une API pour répondre à cette demande, et si nous ne le faisons pas, nous renvoyons un message d'erreur. Si nous avons une API, nous renvoyons le résultat de l'exécution de la fonction à partir de registerRequestHandler , avec le nom de demande respectif.
Pour la réponse, nous créons un objet avec les champs: type = 2, id comme ID du message auquel nous répondons, statut comme un champ qui dit si cette réponse est une erreur (si nous n'avons pas d'API, ou le gestionnaire a généré une erreur, ou l'utilisateur a renvoyé une promesse rejetée, ou une autre erreur se produit (sérialiser)), et le contenu en tant que données de réponse.
Nous avons donc décrit le fonctionnement du protocole, qui exécute la classe Bus mais n'a pas expliqué le processus d'envoi et de réception des messages. Pour cela, nous avons besoin d'adaptateurs de classe avec trois méthodes.
- envoyer est une méthode qui est essentiellement responsable de l'envoi de messages
- addListener est une méthode pour s'abonner aux événements
- destroy est une méthode pour supprimer des abonnements lors de la suppression de Bus .
Adaptateurs. Exécution du protocole
Pour lancer le protocole, actuellement, seul l'adaptateur pour travailler avec iframe / window est prĂȘt. Il utilise postMessage et addEventListener . C'est assez simple: vous devez envoyer un message Ă postMessage avec une origine correcte et Ă©couter les messages via addEventListener sur l'Ă©vĂ©nement "message" .
Nous avons rencontré quelques nuances:
- Vous devez toujours Ă©couter les rĂ©ponses sur VOTRE fenĂȘtre et les envoyer sur la fenĂȘtre AUTRE (iframe, ouvreur, parent, travailleur, etc.). Si vous essayez d'Ă©couter un message dans la fenĂȘtre OTHER et que l'origine diffĂšre de la fenĂȘtre actuelle, une erreur se produit.
- Lors de la rĂ©ception d'un message, assurez-vous qu'il vous a Ă©tĂ© adressĂ©: la fenĂȘtre peut accueillir de nombreux messages d'analyse, WebStorm (si vous l'utilisez) et d'autres iframes, vous devez donc ĂȘtre sĂ»r que l'Ă©vĂ©nement est dans votre protocole et qu'il vous est destinĂ©.
- Vous ne pouvez pas renvoyer une promesse avec une copie de Windows , car la promesse lors du retour du rĂ©sultat tentera de vĂ©rifier si le rĂ©sultat a la mĂ©thode then . Si vous n'avez pas accĂšs Ă la fenĂȘtre (par exemple, une fenĂȘtre avec une autre origine), une erreur se produira (mais pas dans tous les navigateurs). Pour Ă©viter ce problĂšme, il suffirait d'envelopper la fenĂȘtre dans l'objet et de mettre un objet dans la promesse qui a un lien vers la fenĂȘtre correcte.
Exemples d'utilisation:
La bibliothĂšque est disponible dans NPM et vous pouvez facilement l'installer via votre gestionnaire de paquets - @ waves / waves-browser-bus
Pour établir une communication bidirectionnelle avec un iframe, il suffit d'utiliser ce code:
import { Bus, WindowAdapter } from '@waves/waves-browser-bus'; const url = 'https://some-iframe-content-url.com'; const iframe = document.createElement('iframe'); WindowAdapter.createSimpleWindowAdapter(iframe).then(adapter => { const bus = new Bus(adapter); bus.once('ready', () => {
Iframe intérieur:
import { Bus, WindowAdapter } from '@waves/waves-browser-bus'; WindowAdapter.createSimpleWindowAdapter().then(adapter => { const bus = new Bus(adapter); bus.dispatchEvent('ready', null);
Et ensuite?
Nous avons un protocole flexible et polyvalent qui peut ĂȘtre utilisĂ© dans n'importe quelle situation. Ensuite, je prĂ©vois de sĂ©parer les adaptateurs du protocole et de les placer dans des packages npm distincts, et d'ajouter des adaptateurs pour les onglets de travail et de navigateur. Je souhaite que les adaptateurs d'Ă©criture exĂ©cutant le protocole Ă d'autres fins soient aussi simples que possible. Si vous souhaitez rejoindre le processus de dĂ©veloppement ou avoir des idĂ©es concernant les fonctionnalitĂ©s de la bibliothĂšque, vous ĂȘtes invitĂ©s Ă entrer en contact dans le rĂ©fĂ©rentiel .