今天我介绍一下如何开发一个google插件,这篇文章主要是介绍一下整体的框架,具体细节不会过多讲解。
google插件是安装在google浏览器中的扩展程序,这个扩展程序也是基于html、css、js开发而来的,并且浏览器提供了额外的API供扩展程序使用。
目前最新插件API版本是v3,v2版本会逐步被淘汰(网络上大部分插件api教程都是基于v2版本的,v3版本有很多不兼容变化,此次分享为v3版本)
开发的google插件可以应用到以Chromium为内核的浏览器中,如360浏览器、qq浏览器等
google插件可以做什么主要是看google插件提供什么样功能的API:google api
● tabs:浏览器窗口操作功能:增删改查截图等
● cookie:cookie操作功能
● network:网络拦截功能
…
● 书签控制;
● 下载控制;
● 窗口控制;
● 标签控制;
● 网络请求控制,
● 各类事件监听;
插件本身是使用html、js、css构成
1.配置文件主要是配置关于插件的信息,比如版本、icon、权限配置等。
2.后台脚本background.js主要是一个全局的js逻辑,如果你的插件不需要全局的js逻辑,这个文件可不需要
3.交互弹框是插件和用户交互的逻辑,本质就是一个弹窗html
4.content_script是操作网页的js,如果插件没有对网页的操作逻辑也可不需要
后台脚本在浏览器打开时一直存在,在浏览器关闭后卸载。注意background虽然是js但是它并不存在window、document、XHR、DOM等对象,可以理解为background是浏览器环境(background可访问Chrome对象、fetch)
交互弹框popup.js是在插件点击打开弹框后存在,弹框关闭后卸载。它有dom环境,但是它只能访问popup.html的DOM
content_script.js是插入到网页中的js,所以它能访问和操作网页中的DOM元素,content_script使用分为两种方式:静态插入和动态插入
一个基本插件只包含上述的html、js、css。若用原始的方式去开发插件是非常影响效率,我们需要自己去操作很多的dom,并且npm包也不能直接使用
我们可以用npm、ts、react开发插件,然后使用webpack去打包。
这里注意一点:webpack一般只有一个入口文件,但是background.js和content_script.js并不会在popup.js中引入,所以webpack的配置也是多入口配置
const path = require("path");
module.exports = {
entry: {
main: "./src/main.tsx",
content_script: "./src/content_script.ts",
background: "./src/background.ts",
},
output: {
filename: "[name].js",
path: path.resolve(__dirname, "dist"),
clean: true,
},
mode: "production",
resolve: {
extensions: [".js", ".jsx", ".ts", ".tsx"],
alias: {
"@": path.resolve(__dirname, "src"), // 这样配置后 @ 可以指向 src 目录
},
},
// stats: 'summary',
module: {
rules: [
{
test: /\.less$/i,
use: ["style-loader", "css-loader", "less-loader"],
},
{
test: /\.(tsx|ts)$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: {
presets: [
"@babel/preset-react",
[
"@babel/preset-typescript",
{
isTSX: true,
allExtensions: true,
},
],
],
},
},
},
],
},
};
在popup.html中引入打包后的popup.js
当然,在manifest中配置的background和content_script也是打包后的js
插件通信是google插件开发的一个难点,一定要弄清楚几种类型js之间是如何通信的
场景:
1.有个插件功能是点击一个按钮,网页自动滚屏
2.在background中截屏了网页,但是background中没有DOM元素,如何进行下载图片
3.网页中进行了某些操作,将操作结果回显到popup中
…
在开发插件时因为生命周期和环境的不同,我们会去使用不同作用的js:background.js、content_script.js和popup.js。当不同类型的js需要通信时,如何进行通信?
google插件通过通信机制,使不同类型js进行通信。消息可以包含任何有效的 JSON 对象(null、布尔值、数字、字符串、数组或对象)。有两种消息传递 API:一种用于一次性请求,另一种更复杂,用于允许发送多条消息的长期连接。官网google插件通信
content_script发送信息
(async () => {
const response = await chrome.runtime.sendMessage({greeting: "hello"});
// do something with response here, not outside the function
console.log(response);
})();
background或popup接收信息并响应
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
console.log(sender.tab ?
"from a content script:" + sender.tab.url :
"from the extension");
if (request.greeting === "hello")
sendResponse({farewell: "goodbye"});
}
);
background发送信息(需要指定发送到哪个窗口)
(async () => {
const [tab] = await chrome.tabs.query({active: true, lastFocusedWindow: true});
const response = await chrome.tabs.sendMessage(tab.id, {greeting: "hello"});
// do something with response here, not outside the function
console.log(response);
})();
popup/content_script接收和发送信息
var port = chrome.runtime.connect({name: "knockknock"});
port.postMessage({joke: "Knock knock"});
port.onMessage.addListener(function(msg) {
if (msg.question === "Who's there?")
port.postMessage({answer: "Madame"});
else if (msg.question === "Madame who?")
port.postMessage({answer: "Madame... Bovary"});
});
background监听信息
chrome.runtime.onConnect.addListener(function(port) {
console.assert(port.name === "knockknock");
port.onMessage.addListener(function(msg) {
if (msg.joke === "Knock knock")
port.postMessage({question: "Who's there?"});
else if (msg.answer === "Madame")
port.postMessage({question: "Madame who?"});
else if (msg.answer === "Madame... Bovary")
port.postMessage({question: "I don't get it."});
});
});