Chrome Extension通信篇

1,404 阅读3分钟

通信

而对于插件backgroundpopupcontentDevtool是可以相互调用的,具体通信可以看下图:

脚本权限

我们已经了解了存在的几种通信路径,通信意味着各种API的相互调用,因此在实践之前,也需要去了解一点关于chrome 插件的脚本权限

脚本的类型决定着脚本存在什么权限:比如Chrome APIDOM 访问跨域访问原页面JS访问,具体如图:

JS种类可访问的APIDOM访问情况JS访问情况直接跨域
injected script和普通JS无任何差别,不能访问任何扩展API可以访问可以访问不可以
content script只能访问 extension、runtime等部分API可以访问不可以不可以
popup js可访问绝大部分API,除了devtools系列不可直接访问不可以可以
background js可访问绝大部分API,除了devtools系列不可直接访问不可以可以
devtools js只能访问 devtools、extension、runtime等部分API可以访问devtools可以访问devtools不可以

通讯方法

Injected scriptContent scriptPopup scriptBackground script
Injected scriptwindow.postMessgae
Content scriptwindow.postMessgaechrome.runtime.sendMessagechrome.runtime.connectchrome.tabs.sendMessagechrome.runtime.sendMessagechrome.runtime.connect
Popup scriptchrome.tabs.connectchrome.tabs.sendMessagechrome.runtime.sendMessagechrome.runtime.onMessage
Background scriptchrome.runtime.sendMessagechrome.runtime.onMessagechrome.tabs.sendMessagechrome.extension.getViewschrome.runtime.sendMessagechrome.runtime.connect
Devtools scriptchrome.devtools.inspectedwindow.evalchrome.runtime.sendMessagechrome.runtime. sendMessage

popup给background发送消息

background.ts

const add = (...rest: number[]) => rest.reduce((a, b) => a + b, 0)
chrome.runtime.onMessage.addListener((request, _sender, sendResponse) => {
  const {val1, val2} = request;
  console.log('background.js收到popup.js信息')
  sendResponse({res: add(val1, val2)});
});

popup.tsx

import { Button } from '@arco-design/web-react';
import React, { useState } from 'react';
const App = () => {
  const [num, setNumber] = useState<number | undefined>()
  const handleSendBackgroundMessage = () => {
    chrome.runtime.sendMessage({val1: 1, val2: 2}, (response) => {
      console.log('popup.js收到background.js信息')
      setNumber(response.res);
    });
  }
  return (
    <div>
      <Button onClick={handleSendBackgroundMessage}>Background通信</Button>
      <div>计算1+2=<span>{num}</span></div>
    </div>
  );
};
export default App;

background给popup发送消息

由于不激活popup是不会加载popup.js的,所以需要要在popup.js激活状态下进行

background.ts

chrome.tabs.onActivated.addListener(() => {
  chrome.runtime.sendMessage({ val1: 1, val2: 2 }, (response) => {
    console.log('background.js收到popup.js信息', response);
  });
});

popup.tsx

import { Button } from '@arco-design/web-react';
import React, { useLayoutEffect, useState } from 'react';
import { add } from 'lodash';

const App = () => {
  useLayoutEffect(() => {
    chrome.runtime.onMessage.addListener((request, _sender, sendResponse) => {
      const { val1, val2 } = request;
      console.log('popup.js收到background.js信息 ---> 接受');
      sendResponse({ res: add(val1, val2) });
    });
  }, []);
  return (
    <div>
        激活我
    </div>
  );
};
export default App;

content给background发送消息

background.ts

const add = (...rest: number[]) => rest.reduce((a, b) => a + b, 0);
chrome.runtime.onMessage.addListener((request, _sender, sendResponse) => {
  const { val1, val2, from } = request;
  if (from === 'content') {
    console.log('background.js收到content.js信息');
    return sendResponse({ res: add(val1, val2) });
  }
});

content.tsx

import { Button } from '@arco-design/web-react';
import React, { useState } from 'react';
const App = () => {
  const [num, setNumber] = useState<number | undefined>()
  const handleSendBackgroundMessage = () => {
    chrome.runtime.sendMessage({val1: 1, val2: 2}, (response) => {
      console.log('content.js收到background.js信息')
      setNumber(response.res);
    });
  }
  return (
    <div>
      <Button onClick={handleSendBackgroundMessage}>Background通信</Button>
      <div>计算1+2=<span>{num}</span></div>
    </div>
  );
};
export default App;

background给content发送消息

由于content.js可能存在多个tab下,所以要和某个tab下content.js通信,要找到当前tab

background.ts

chrome.tabs.onActivated.addListener(() => {
  chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
    chrome.tabs.sendMessage(tabs[0].id!, { from: 'background.js', val1: 1, val2: 2 }, (response) => {
      console.log('background -> content script infos have been sended', response);
    });
  });
});

content.tsx

/* eslint-disable no-undef */
import React, { useLayoutEffect } from 'react';
import { Button } from '@arco-design/web-react';
const App = () => {
  useLayoutEffect(() => {
    chrome.runtime.onMessage.addListener((request, _sender, sendResponse) => {
      const { val1, val2 } = request;
      console.log('content.js收到background.js信息 ---> 接受');
      sendResponse({ res: add(val1, val2) });
    });
  }, []);
  return (
    <div className="flex flex-col">
      <Button onClick={handleSendBackgroundMessage}>Background通信</Button>
      <div>
        计算1+2=<span>{num}</span>
      </div>
    </div>
  );
};
export default App;

popup给content发送消息 & content给popup发送消息

由于content.js可能存在多个tab下,所以要和某个tab下content.js通信,要找到当前tab

content.tsx

/* eslint-disable no-undef */
import React, { useLayoutEffect } from 'react';
import { Button } from '@arco-design/web-react';
import { useState } from 'react';
import { add } from 'lodash';
const App = () => {
  const [num1, setNumber1] = useState<number | undefined>();
  const handleSendPopupMessage = () => {
    chrome.runtime.sendMessage({ val1: 5, val2: 3, from: 'content' }, (response) => {
      console.log('content.js收到popup.js信息 ---> 发起');
      setNumber1(response.res);
    });
  };
  useLayoutEffect(() => {
    chrome.runtime.onMessage.addListener((request, _sender, sendResponse) => {
      const { val1, val2, from } = request;
      if (from === 'popup') {
        console.log('content.js收到popup.js信息 ---> 接受');
        return sendResponse({ res: add(val1, val2) });
      }
    });
  }, []);
  return (
    <div className="flex flex-col">
      <Button onClick={handleSendPopupMessage}>Popup通信</Button>
      <div>
        计算3+5=<span>{num1}</span>
      </div>
    </div>
  );
};
export default App;

popup.tsx

/* eslint-disable no-undef */
import { Button } from '@arco-design/web-react';
import React, { useLayoutEffect, useState } from 'react';
import { add } from 'lodash';
const getCurrentTab = async () => {
  let queryOptions = { active: true, currentWindow: true };
  let [tab] = await chrome.tabs.query(queryOptions);
  return tab;
};

const App = () => {
  const [num1, setNumber1] = useState<number | undefined>();
  const handleSendContentMessage = async () => {
    const tab = await getCurrentTab();
    chrome.tabs.sendMessage(tab.id!, { from: 'popup', val1: 5, val2: 2 }, (response) => {
      console.log('popup.js收到content.js信息 ---> 发起');
      setNumber1(response.res);
    });
  };
  useLayoutEffect(() => {
    chrome.runtime.onMessage.addListener((request, _sender, sendResponse) => {
      const { val1, val2, from } = request;
      if (from === 'content') {
        console.log('popup.js收到content.js信息 ---> 接受');
        return sendResponse({ res: add(val1, val2) });
      }
    });
  }, []);
  return (
    <div>
      <Button onClick={handleSendContentMessage}>Content通信</Button>
      <div>
        计算5+2=<span>{num1}</span>
      </div>
    </div>
  );
};
export default App;

DevTools Page和background通信

放到DevTools介绍

demo地址

demo地址