どのようにコンテンツのスクリプト、ポップアップ、およびバックグラウンドのブラウザの拡張機能の開発の間で通信する


ブラウザ拡張機能の異なる部分の間のメッセージパッシングは、ブラウザの拡張機能の開発から始まるとき、最も混乱している部分です.この投稿は、コンテンツのスクリプト、背景、およびポップアップ(ブラウザ動作)の間で通信するために、どのように私のWeb拡張モジュールを構成するかについてです.
これらは我々が使用する部分です.
  • ファイル内で使用するメッセージのIDを作成します.通常のオブジェクトリテラルまたはenum を使用する場合.
  • マッピングを作成するMap<MessageID, callback> そして、そのIDを持つメッセージが到着したときに実行するコールバックで、ステップ1で作成したメッセージIDを修正します.
  • メッセージリスナーを登録します.アイテムのループをMap ステップ2で作成したリスナーを追加しますvalue 各キーについてMessageID ).

  • コードを書きましょう
    完成したコードはweb-extension-communication-blog-post . 私はあなたのリンクを開いて、私と一緒に従ってください.また、FirefoxとChromeのAPIの違いに対処する必要がないので、ポリフィルを使用します.また、PolyFillは両方のFirefoxとChromeのための見込みベースAPIを許します.使用中webextension-polyfill-ts Mozilla ' sのためのタイプスクリプトラッパーですwebextension-polyfill .
    私たちのメッセージは簡単になります.コンテンツスクリプトと背景の間に「hi」または「bye」を交換します.
    まず、コンテンツスクリプトと背景の間にメッセージを送信するために使用できるユーティリティ関数を2つ書きます.
    // Messenger.ts
    import { browser } from "webextension-polyfill-ts";
    
    const Messenger = {
      /**
       * Send a message to Background script
       *
       * @param {BackgroundMessage} type Background Message Type
       * @param {*} [data=null]
       * @return {*}
       */
      async sendMessageToBackground(type, data = null) {
        try {
          const response = await browser.runtime.sendMessage({ type, data });
          return response;
        } catch (error) {
          console.error("sendMessageToBackground error: ", error);
          return null;
        }
      },
    
      /**
       * Send a message to Content Script of a Tab
       *
       * @param {number} tabID Tab ID
       * @param {ContentScriptMessage} type
       * @param {*} [data=null]
       * @return {*}
       */
      async sendMessageToContentScript(tabID, type, data = null) {
        try {
          // Notice the API difference - browser.tabs to send to content script but browser.runtime to send to background.
          const response = await browser.tabs.sendMessage(tabID, { type, data });
          console.log("response:", response);
          return response;
        } catch (error) {
          console.error("sendMessageToContentScript error: ", error);
          return null;
        }
      },
    };
    
    私は、我々が常に投げる必要がないので、別々のファイルにこれらの2つの機能を置くのが好きですbrowser.tabs or browser.runtime どこでもAPI.我々はクリーンなコードを得るMessenger.sendMessageToBackground and Messenger.sendMessageToContentScript 関数.
    手順1で、各タイプのメッセージのIDを作成します.私はタイプスクリプトを使っていますenums 彼らは簡単に機能を入力するが、同様に単純なオブジェクトを使用することができます.IDは- 1 , 2 ,
    // messages.ts
    export enum ContentScriptMessages {
      SAY_HELLO_TO_CS,
      SAY_BYE_TO_CS,
    }
    
    export enum BackgroundMessages {
      SAY_HELLO_TO_BG,
      SAY_BYE_TO_BG,
    }
    
    コンテンツスクリプトやポップアップからバックグラウンドスクリプトと話をする必要があるときはいつでもMessenger.sendMessageToBackground(BackgroundMessages.SAY_HELLO_TO_BG, {message: "Hey Background"}) .
    バックグラウンドからのコンテンツスクリプトへのメッセージの送信も、コンテンツスクリプトのタブIDを渡す必要があることに似ています.それはあなたが見る最初のパラメータですMessenger.sendMessageToContentScript(tabID, ContentScriptMessages.SAY_HELLO_TO_CS, {message: "Hey Content Script!"}) 関数.

    メッセージリスナーの登録
    ステップ2と3で話したメッセージリスナーを登録します.このコードは、コンテンツスクリプトと背景の両方に似ています.登録しますContentScriptMessages コンテンツスクリプト初期化とBackgroundMessages バックグラウンド初期化.
    // content-script.ts
    // Install webextension-polyfill for JavaScript based projects
    import { browser } from "webextension-polyfill-ts";
    import { BackgroundMessages, ContentScriptMessages } from "./messages";
    import Messenger from "./Messenger";
    
    class ContentScript {
      requests = new Map();
    
      async receiveHello(sender, data) {
        console.log(`receiveHelloFromBackground: `, data);
      }
    
      async receiveBye(sender, data) {
        console.log(`receiveByeFromBackground: `, data);
      }
    
      async sayHelloToBackground() {
        const response = await Messenger.sendMessageToBackground(
          BackgroundMessages.SAY_HELLO_TO_BG,
          { message: "Hello Background!!!" }
        );
        console.log("Background Response: ", response);
      }
    
      async sayByeToBackground() {
        await Messenger.sendMessageToBackground(BackgroundMessages.SAY_BYE_TO_BG, {
          message: "Bye Background!!!",
        });
      }
    
      registerMessengerRequests() {
        this.requests.set(ContentScriptMessages.SAY_HELLO_TO_CS, this.receiveHello);
        this.requests.set(ContentScriptMessages.SAY_BYE_TO_CS, this.receiveBye);
      }
    
      listenForMessages() {
        browser.runtime.onMessage.addListener((message, sender) => {
          const { type, data } = message;
          return this.requests.get(type)(sender, data);
        });
      }
    
      init() {
        // 1. Create a mapping for message listeners
        this.registerMessengerRequests();
    
        // 2. Listen for messages from background and run the listener from the map
        this.listenForMessages();
      }
    }
    
    new ContentScript().init();
    
    //background.ts
    import { browser, Runtime } from "webextension-polyfill-ts";
    import { BackgroundMessages, ContentScriptMessages } from "./messages";
    import Messenger from "./Messenger";
    import { IMessage, MessageListener } from "./types";
    
    class Background {
      requests = new Map<BackgroundMessages, MessageListener>();
    
      async receiveHello(sender: Runtime.MessageSender, data: IMessage<any>) {
        console.log("receiveHelloFromContentScript: ", data);
        return {
          message: "Hey there!!!",
        };
      }
    
      async receiveBye(sender: Runtime.MessageSender, data: IMessage<any>) {
        console.log("receiveByeFromContentScript: ", data);
        return {
          message: "Bye there!!!",
        };
      }
    
      async sayHelloToContentScript(tabID: number) {
        await Messenger.sendMessageToContentScript(
          tabID,
          ContentScriptMessages.SAY_HELLO_TO_CS,
          { message: "Hello from BG!!!" }
        );
      }
    
      async sayByeToContentScript(tabID: number) {
        await Messenger.sendMessageToContentScript(
          tabID,
          ContentScriptMessages.SAY_BYE_TO_CS,
          { message: "Bye from BG!!!" }
        );
      }
    
      registerMessengerRequests() {
        this.requests.set(BackgroundMessages.SAY_HELLO_TO_BG, this.receiveHello);
        this.requests.set(BackgroundMessages.SAY_BYE_TO_BG, this.receiveBye);
      }
    
      listenForMessages() {
        browser.runtime.onMessage.addListener((message, sender) => {
          const { type, data } = message;
          return this.requests.get(type)(sender, data);
        });
      }
      // Example: Send message to content script of active tab
      sendHelloToActiveTab() {
        browser.tabs.query({ active: true, currentWindow: true }).then((tabs) => {
          tabs.forEach((tab) => {
            this.sayHelloToContentScript(tab.id);
          });
        });
      }
    
      init() {
        // 1. Create a mapping for message listeners
        this.registerMessengerRequests();
    
        // 2. Listen for messages from background and run the listener from the map
        this.listenForMessages();
      }
    }
    
    new Background().init();
    
    私たちはmessages.ts and Messenger.ts ポップアップからも.ポップアップはすべての時間を開くことはありませんので、我々はそこにメッセージリスナーを追加する必要はありません.私は、使用を好むMessenger.sendMessageToBackground とポップアップの戻り値を使用します.

    結論
    我々は、メッセージングのほとんどを抽象化しましたmessages.ts and Messenger.ts . 新しいタイプのメッセージを追加するたびにenum (またはオブジェクトを使用した場合にキーを追加する)messages.ts そして、内容スクリプトまたはバックグラウンドでリスナーを加えますregisterMessengerRequests 関数.
    このコードはFirefoxとすべてのクロムベースのブラウザで動作します.単にメッセージを送信してawait 他の側がリスナーから何かを返すならば、応答のために.Mozillaのおかげでwebextension-polyfill 我々はクロスブラウザのサポートを取得し、ChromeのAPIのコールバックバージョンに対処する必要はありません.
    このように解決しようとしている他の方法がありますwebext-redux 拡張子の異なる部分の間の状態を管理することとともにメッセージを渡すための賢い方法ですredux ウェイ.しかし、私はそれが既存の複雑さを解決して、反応だけで働く試みで、さらなる冗長なAPIを加えると感じます.お気軽にそのリポジトリをチェックしてお客様の要件に合う場合.
    このブログ記事のために構築したサンプル拡張子をインストールできますhere .
    良い一日を!👋