admin管理员组

文章数量:1130349

基本介绍

开发浏览器插件,首先需要先了解他的结构,浏览器扩展通常包括以下几个部分

├── manifest.json
├── package.json
├── vite.config.js
├── src
├── background
│ └── index.js
├── content
│ └── content.js
├── icons
│ └── icon_16x16.png
│ └── icon_48x48.png
│ └── icon_128x128.png
├── popup
├── App.vue
├── index.html
├── main.ts
└── style.css

manifest.json:扩展的配置文件,是插件的入口文件

background.jsbackground 目录:包含扩展的后台脚本,它在插件安装时就会加载,并且在浏览器运行期间始终保持活动状态

content:在网页中注入的脚本

popup:用户界面部分,是插件弹框页面

icons 是放置插件的 16、48、128 的 png 图片

manifest.json 配置
{
  "manifest_version": 2,
  "name": "My Browser Extension",
  "version": "1.0",
  "background": {
    "scripts": ["background/index.js"]
  },
  "permissions": [
    "activeTab",
    "storage"
  ],
  "icons": {
        "16": "icon.png",
        "48": "icon.png",
        "128": "icon.png"
   },
  "content_scripts": [
    {
      "matches": ["<all_urls>"],
      "js": ["content/content.js"]
    }
  ]
}

配置说明:

manifest_version:指定了manifest.json文件的版本号为2,表示遵循Manifest V2规范。
name:指定插件的名称。
version:指定插件的版本号为"1.0.0"。
description:描述插件的简要描述,这里是空字符串,可以填写插件的功能介绍等信息。
author:指定插件的作者。
icons:包含不同尺寸的图标文件路径,用于在浏览器中显示插件图标。
options_page:指定插件的选项页面为"options.html"。
page_action:配置页面操作相关的信息,包括默认图标、标题和弹出页面。
content_security_policy:设置内容安全策略,限制了脚本和对象的来源,以增强安全性。
background:配置后台页面的持久性和后台脚本文件路径。
content_scripts:定义内容脚本,指定了脚本文件路径、匹配的URL以及运行时机。
permissions:列出插件需要的权限,包括处理声明式内容、标签、活动标签、Cookie、网络请求、存储等。
web_accessible_resources:指定可以从插件外部访问的资源,这里是"fonts/*",表示可以访问fonts文件夹下的所有资源。

创建项目
npm install -g @vue/cli
vue create my-browser-extension
cd my-browser-extension
消息通信

5种类型的JS对比

一、popup.jsservice-worker.js

弹出页面(popup.js) 和 背景脚本(service-worker.js) 之间的通信可以通过chrome.runtime.sendMessage 来实现

1.popup.jsservice-worker.js 发送消息

// service-worker.js
chrome.runtime.onMessage.addListener(function (message, sender, sendResponse) {
    if (message.type === 'popupSendMessageToServiceWorker') {
        sendResponse({ success: true, message: 'service-worker.js收到消息' });
    }
});

// popup.js
chrome.runtime.sendMessage({ type: 'popupSendMessageToServiceWorker' }, function(response) {
  console.log(response);  // 输出从service-worker.js返回的数据
});

2.service-worker.jspopup.js 发送消息
背景脚本(service-worker.js)向弹出页面(popup.js) 发送消息

// popup.js
chrome.runtime.onMessage.addListener(function (message, sender, sendResponse) {
  if (message.type === 'serviceWorkerSendMessageToPopup') {
    sendResponse({ success: true, message: 'popup.js收到消息' });
  }
});

// service-worker.js
chrome.runtime.sendMessage({type: 'serviceWorkerSendMessageToPopup'}, function (response) {
   console.log(response);  // 输出从popup.js返回的数据
})
二、content.jsservice-worker.js

content.jsservice-worker.js 通常通过 chrome.runtime.sendMessagechrome.tabs.sendMessage 进行消息传递。

1.content.jsservice-worker.js 发送消息

// service-worker.js
chrome.runtime.onMessage.addListener(function (message, sender, sendResponse) {
    if (message.type === 'contentSendMessageToServiceWorker') {
        sendResponse({ success: true, message: 'service-worker.js收到消息' });
    }
});

// content.js
chrome.runtime.sendMessage({ type: 'contentSendMessageToServiceWorker' }, function(response) {
    console.log(response);
});

2.service-worker.jscontent.js 发送消息

//manifest.json 如需使用 tabs API,请在扩展程序manifest中声明 "tabs" 权限。
{
  "permissions": [
    "tabs"
  ]
}

// service-worker.js
chrome.tabs.query({ active: true, currentWindow: true }, function(tabs) {
    chrome.tabs.sendMessage(tabs[0].id, { greeting: 'Hello from background!' }, function (response) {
        console.log(response);  // 输出从content.js返回的数据
    })
})

// content.js
chrome.runtime.onMessage.addListener(function (message, sender, sendResponse) {
    if (message.greeting) {
        console.log(message.greeting);  // 输出: Hello from background!
        sendResponse({ response: 'Message received!' });
    }
});
三、content.jspopup.js

content_scriptspopup 主动发消息的前提是 popup 必须打开!否则需要利用 background 作中转。

如果 backgroundpopup 同时监听,那么它们都可以同时收到消息,但是只有一个可以 sendResponse ,一个先发送了,那么另外一个再发送就无效

使用和二的一样

// content.js
chrome.tabs.query({ active: true, currentWindow: true }, function(tabs) {
    chrome.tabs.sendMessage(tabs[0].id, { greeting: 'Hello from background!' }, function (response) {
        console.log(response);  // 输出从popup.js返回的数据
    })
})

// popup.js
chrome.runtime.onMessage.addListener(function (message, sender, sendResponse) {
    if (message.greeting) {
        console.log(message.greeting);  // 输出: Hello from background!
        sendResponse({ response: 'Message received!' });
    }
});
四、通过 chrome.storage 共享数据

有时候可能需要在多个部分之间共享数据,chrome.storage 是一个跨脚本的存储机制,可以让背景脚本、弹出页面和内容脚本共享数据。这个类似于网页中的local storage,很好理解

存储数据:

//manifest.json 如需使用 Storage API,请在扩展程序manifest中声明 "storage" 权限。
{
  "permissions": [
    "storage"
  ]
}

// background.js
// service-worker.js存储数据
chrome.storage.local.set({ key1: 'value1' }, function () {
    console.log('Data saved!');
});
chrome.storage.local.set({ key2: true, key3: {key4: 'value4'} }, function () {
    console.log('Data saved!');
});

读取数据:

// popup.js
chrome.storage.local.get('key1', function (result) {
  console.log('result is ' + JSON.stringify(result));
  console.log('Value currently is ' + result.key1);
});
chrome.storage.local.get(['key2'], function (result) {
  console.log('result is ' + JSON.stringify(result));
  console.log('Value currently is ' + result.key2);
});
chrome.storage.local.get(['key1','key2'], function (result) {
  console.log('result is ' + JSON.stringify(result));
  console.log('Value currently is ' + result.key2);
});
chrome.storage.local.get({key3: {} }, function (result) {
  console.log('result is ' + JSON.stringify(result));
  console.log('Value currently is ' + result.key3.key4);
});
chrome.storage.local.get({key5: true , key6:false}, function (result) {
  console.log('result is ' + JSON.stringify(result));
  console.log('Value currently is ' + result.key5 + ',' + result.key6);
});

删除数据:

//删除数据
chrome.storage.local.remove(['key1'], function() {
    console.log('Key1 removed successfully.');
});

清除所有的数据:

//清除所有数据
chrome.storage.local.clear(function() {
    console.log('All data cleared.');
});

监听数据变化:

//content.js
// 监听存储变化(两种方法都可以)
//chrome.storage.local.onChanged.addListener(function(changes) {
//    console.log('Storage area "local" has changed');
//    console.log(changes);
//});

chrome.storage.onChanged.addListener(function(changes, area) {
    if (area === 'local') {
        console.log('Storage area "local" has changed');
        console.log(changes);
    }
});

setTimeout(function (){
    chrome.storage.local.set({ key2: false }, function () {
        console.log('Data saved!');
    });
},5000)
五、使用 chrome.runtime.connect 和长连接

除了单次消息发送接收,chrome.runtime.connect 可以创建一个持久的连接,适用于需要长期通信的场景。

1.popup.jsservice-worker.js 长连接

// service-worker.js
chrome.runtime.onConnect.addListener(function(port) {
    console.log('Connected:', port.name);
    port.onMessage.addListener(function(msg) {
        if (msg.action === 'getUserInfo') {
            // 模拟从后台获取用户信息
            const userInfo = { name: 'John Doe', age: 30 };
            port.postMessage({ data: userInfo });
        }
    });
    port.onDisconnect.addListener(function() {
        console.log('Port disconnected');
    });
});

// popup.js
let port = chrome.runtime.connect({ name: 'popup' });
// 请求获取用户信息
port.postMessage({ action: 'getUserInfo' });
// 监听来自service-worker.js的消息
port.onMessage.addListener(function(msg) {
  console.log('Received user info:', msg.data);
});

2.content.jspopup.js 长连接

// popup.js
chrome.tabs.query({ active: true, currentWindow: true }, function(tabs) {
  let port = chrome.tabs.connect(tabs[0].id, {name: 'test-connect'});
  port.postMessage({question: '你是谁啊?'});
  port.onMessage.addListener(function(msg) {
    alert('收到消息:'+msg.answer);
    if(msg.answer && msg.answer.startsWith('我是'))
    {
      port.postMessage({question: '哦,原来是你啊!'});
    }
  });
});

// content.js
chrome.runtime.onConnect.addListener(function(port) {
    console.log(port);
    if(port.name === 'test-connect') {
        port.onMessage.addListener(function(msg) {
            console.log('收到长连接消息:', msg);
            if(msg.question === '你是谁啊?') port.postMessage({answer: '我是容易摔倒的猪!'});
        });
    }
});

实现三方长连接方式:

content.jsservice-worker.js 通过 chrome.runtime.connect 建立连接。
popup.js 也与 service-worker.js 进行通信。
service-worker.js 作为中介,协调 content.jspopup.js 的消息。

六、插件使用限制

1、content.js的脚本限制,在manifest.json中的content_scripts设置matches中执行的域名。

  "content_scripts": [
    {
      "js": ["js/content.js"],
      "matches": ["http://wangpu.taobao/*", "https://wangpu.taobao/*", "http://myseller.taobao/*", "https://myseller.taobao/*"],
      "run_at": "document_idle"
    }
  ]

2、插件的popup页面是否显示
background.js中通过 window.chrome.declarativeContent.onPageChanged.addRules添加域名限制。

window.chrome.runtime.onInstalled.addListener(function () {
  window.chrome.declarativeContent.onPageChanged.removeRules(undefined, function () {
    window.chrome.declarativeContent.onPageChanged.addRules([
      {
        conditions: [
          new window.chrome.declarativeContent.PageStateMatcher({
            pageUrl: { urlContains: 'wangpu.taobao' }
          })
        ],
        actions: [new window.chrome.declarativeContent.ShowPageAction()]
      }
    ])
  })
})

window.chrome.declarativeContent是Chrome浏览器扩展程序中用于定义内容脚本的API。其中,urlContains、urlMatches和urlPrefix是用于匹配网页URL的条件。

  • urlContains:表示URL包含指定的字符串时条件匹配。例如,如果指定urlContains: ‘example’,则只有当网页URL中包含"example"时条件才会匹配。
  • urlMatches:表示URL匹配指定的正则表达式时条件匹配。例如,如果指定urlMatches: > ‘https://www.example/*’,则只有当网页URL以"https://www.example/"开头时条件才会匹配。
  • urlPrefix:表示URL以指定的字符串开头时条件匹配。例如,如果指定urlPrefix: ‘https://example’,则只有当网页URL以"https://example"开头时条件才会匹配。
基本介绍

开发浏览器插件,首先需要先了解他的结构,浏览器扩展通常包括以下几个部分

├── manifest.json
├── package.json
├── vite.config.js
├── src
├── background
│ └── index.js
├── content
│ └── content.js
├── icons
│ └── icon_16x16.png
│ └── icon_48x48.png
│ └── icon_128x128.png
├── popup
├── App.vue
├── index.html
├── main.ts
└── style.css

manifest.json:扩展的配置文件,是插件的入口文件

background.jsbackground 目录:包含扩展的后台脚本,它在插件安装时就会加载,并且在浏览器运行期间始终保持活动状态

content:在网页中注入的脚本

popup:用户界面部分,是插件弹框页面

icons 是放置插件的 16、48、128 的 png 图片

manifest.json 配置
{
  "manifest_version": 2,
  "name": "My Browser Extension",
  "version": "1.0",
  "background": {
    "scripts": ["background/index.js"]
  },
  "permissions": [
    "activeTab",
    "storage"
  ],
  "icons": {
        "16": "icon.png",
        "48": "icon.png",
        "128": "icon.png"
   },
  "content_scripts": [
    {
      "matches": ["<all_urls>"],
      "js": ["content/content.js"]
    }
  ]
}

配置说明:

manifest_version:指定了manifest.json文件的版本号为2,表示遵循Manifest V2规范。
name:指定插件的名称。
version:指定插件的版本号为"1.0.0"。
description:描述插件的简要描述,这里是空字符串,可以填写插件的功能介绍等信息。
author:指定插件的作者。
icons:包含不同尺寸的图标文件路径,用于在浏览器中显示插件图标。
options_page:指定插件的选项页面为"options.html"。
page_action:配置页面操作相关的信息,包括默认图标、标题和弹出页面。
content_security_policy:设置内容安全策略,限制了脚本和对象的来源,以增强安全性。
background:配置后台页面的持久性和后台脚本文件路径。
content_scripts:定义内容脚本,指定了脚本文件路径、匹配的URL以及运行时机。
permissions:列出插件需要的权限,包括处理声明式内容、标签、活动标签、Cookie、网络请求、存储等。
web_accessible_resources:指定可以从插件外部访问的资源,这里是"fonts/*",表示可以访问fonts文件夹下的所有资源。

创建项目
npm install -g @vue/cli
vue create my-browser-extension
cd my-browser-extension
消息通信

5种类型的JS对比

一、popup.jsservice-worker.js

弹出页面(popup.js) 和 背景脚本(service-worker.js) 之间的通信可以通过chrome.runtime.sendMessage 来实现

1.popup.jsservice-worker.js 发送消息

// service-worker.js
chrome.runtime.onMessage.addListener(function (message, sender, sendResponse) {
    if (message.type === 'popupSendMessageToServiceWorker') {
        sendResponse({ success: true, message: 'service-worker.js收到消息' });
    }
});

// popup.js
chrome.runtime.sendMessage({ type: 'popupSendMessageToServiceWorker' }, function(response) {
  console.log(response);  // 输出从service-worker.js返回的数据
});

2.service-worker.jspopup.js 发送消息
背景脚本(service-worker.js)向弹出页面(popup.js) 发送消息

// popup.js
chrome.runtime.onMessage.addListener(function (message, sender, sendResponse) {
  if (message.type === 'serviceWorkerSendMessageToPopup') {
    sendResponse({ success: true, message: 'popup.js收到消息' });
  }
});

// service-worker.js
chrome.runtime.sendMessage({type: 'serviceWorkerSendMessageToPopup'}, function (response) {
   console.log(response);  // 输出从popup.js返回的数据
})
二、content.jsservice-worker.js

content.jsservice-worker.js 通常通过 chrome.runtime.sendMessagechrome.tabs.sendMessage 进行消息传递。

1.content.jsservice-worker.js 发送消息

// service-worker.js
chrome.runtime.onMessage.addListener(function (message, sender, sendResponse) {
    if (message.type === 'contentSendMessageToServiceWorker') {
        sendResponse({ success: true, message: 'service-worker.js收到消息' });
    }
});

// content.js
chrome.runtime.sendMessage({ type: 'contentSendMessageToServiceWorker' }, function(response) {
    console.log(response);
});

2.service-worker.jscontent.js 发送消息

//manifest.json 如需使用 tabs API,请在扩展程序manifest中声明 "tabs" 权限。
{
  "permissions": [
    "tabs"
  ]
}

// service-worker.js
chrome.tabs.query({ active: true, currentWindow: true }, function(tabs) {
    chrome.tabs.sendMessage(tabs[0].id, { greeting: 'Hello from background!' }, function (response) {
        console.log(response);  // 输出从content.js返回的数据
    })
})

// content.js
chrome.runtime.onMessage.addListener(function (message, sender, sendResponse) {
    if (message.greeting) {
        console.log(message.greeting);  // 输出: Hello from background!
        sendResponse({ response: 'Message received!' });
    }
});
三、content.jspopup.js

content_scriptspopup 主动发消息的前提是 popup 必须打开!否则需要利用 background 作中转。

如果 backgroundpopup 同时监听,那么它们都可以同时收到消息,但是只有一个可以 sendResponse ,一个先发送了,那么另外一个再发送就无效

使用和二的一样

// content.js
chrome.tabs.query({ active: true, currentWindow: true }, function(tabs) {
    chrome.tabs.sendMessage(tabs[0].id, { greeting: 'Hello from background!' }, function (response) {
        console.log(response);  // 输出从popup.js返回的数据
    })
})

// popup.js
chrome.runtime.onMessage.addListener(function (message, sender, sendResponse) {
    if (message.greeting) {
        console.log(message.greeting);  // 输出: Hello from background!
        sendResponse({ response: 'Message received!' });
    }
});
四、通过 chrome.storage 共享数据

有时候可能需要在多个部分之间共享数据,chrome.storage 是一个跨脚本的存储机制,可以让背景脚本、弹出页面和内容脚本共享数据。这个类似于网页中的local storage,很好理解

存储数据:

//manifest.json 如需使用 Storage API,请在扩展程序manifest中声明 "storage" 权限。
{
  "permissions": [
    "storage"
  ]
}

// background.js
// service-worker.js存储数据
chrome.storage.local.set({ key1: 'value1' }, function () {
    console.log('Data saved!');
});
chrome.storage.local.set({ key2: true, key3: {key4: 'value4'} }, function () {
    console.log('Data saved!');
});

读取数据:

// popup.js
chrome.storage.local.get('key1', function (result) {
  console.log('result is ' + JSON.stringify(result));
  console.log('Value currently is ' + result.key1);
});
chrome.storage.local.get(['key2'], function (result) {
  console.log('result is ' + JSON.stringify(result));
  console.log('Value currently is ' + result.key2);
});
chrome.storage.local.get(['key1','key2'], function (result) {
  console.log('result is ' + JSON.stringify(result));
  console.log('Value currently is ' + result.key2);
});
chrome.storage.local.get({key3: {} }, function (result) {
  console.log('result is ' + JSON.stringify(result));
  console.log('Value currently is ' + result.key3.key4);
});
chrome.storage.local.get({key5: true , key6:false}, function (result) {
  console.log('result is ' + JSON.stringify(result));
  console.log('Value currently is ' + result.key5 + ',' + result.key6);
});

删除数据:

//删除数据
chrome.storage.local.remove(['key1'], function() {
    console.log('Key1 removed successfully.');
});

清除所有的数据:

//清除所有数据
chrome.storage.local.clear(function() {
    console.log('All data cleared.');
});

监听数据变化:

//content.js
// 监听存储变化(两种方法都可以)
//chrome.storage.local.onChanged.addListener(function(changes) {
//    console.log('Storage area "local" has changed');
//    console.log(changes);
//});

chrome.storage.onChanged.addListener(function(changes, area) {
    if (area === 'local') {
        console.log('Storage area "local" has changed');
        console.log(changes);
    }
});

setTimeout(function (){
    chrome.storage.local.set({ key2: false }, function () {
        console.log('Data saved!');
    });
},5000)
五、使用 chrome.runtime.connect 和长连接

除了单次消息发送接收,chrome.runtime.connect 可以创建一个持久的连接,适用于需要长期通信的场景。

1.popup.jsservice-worker.js 长连接

// service-worker.js
chrome.runtime.onConnect.addListener(function(port) {
    console.log('Connected:', port.name);
    port.onMessage.addListener(function(msg) {
        if (msg.action === 'getUserInfo') {
            // 模拟从后台获取用户信息
            const userInfo = { name: 'John Doe', age: 30 };
            port.postMessage({ data: userInfo });
        }
    });
    port.onDisconnect.addListener(function() {
        console.log('Port disconnected');
    });
});

// popup.js
let port = chrome.runtime.connect({ name: 'popup' });
// 请求获取用户信息
port.postMessage({ action: 'getUserInfo' });
// 监听来自service-worker.js的消息
port.onMessage.addListener(function(msg) {
  console.log('Received user info:', msg.data);
});

2.content.jspopup.js 长连接

// popup.js
chrome.tabs.query({ active: true, currentWindow: true }, function(tabs) {
  let port = chrome.tabs.connect(tabs[0].id, {name: 'test-connect'});
  port.postMessage({question: '你是谁啊?'});
  port.onMessage.addListener(function(msg) {
    alert('收到消息:'+msg.answer);
    if(msg.answer && msg.answer.startsWith('我是'))
    {
      port.postMessage({question: '哦,原来是你啊!'});
    }
  });
});

// content.js
chrome.runtime.onConnect.addListener(function(port) {
    console.log(port);
    if(port.name === 'test-connect') {
        port.onMessage.addListener(function(msg) {
            console.log('收到长连接消息:', msg);
            if(msg.question === '你是谁啊?') port.postMessage({answer: '我是容易摔倒的猪!'});
        });
    }
});

实现三方长连接方式:

content.jsservice-worker.js 通过 chrome.runtime.connect 建立连接。
popup.js 也与 service-worker.js 进行通信。
service-worker.js 作为中介,协调 content.jspopup.js 的消息。

六、插件使用限制

1、content.js的脚本限制,在manifest.json中的content_scripts设置matches中执行的域名。

  "content_scripts": [
    {
      "js": ["js/content.js"],
      "matches": ["http://wangpu.taobao/*", "https://wangpu.taobao/*", "http://myseller.taobao/*", "https://myseller.taobao/*"],
      "run_at": "document_idle"
    }
  ]

2、插件的popup页面是否显示
background.js中通过 window.chrome.declarativeContent.onPageChanged.addRules添加域名限制。

window.chrome.runtime.onInstalled.addListener(function () {
  window.chrome.declarativeContent.onPageChanged.removeRules(undefined, function () {
    window.chrome.declarativeContent.onPageChanged.addRules([
      {
        conditions: [
          new window.chrome.declarativeContent.PageStateMatcher({
            pageUrl: { urlContains: 'wangpu.taobao' }
          })
        ],
        actions: [new window.chrome.declarativeContent.ShowPageAction()]
      }
    ])
  })
})

window.chrome.declarativeContent是Chrome浏览器扩展程序中用于定义内容脚本的API。其中,urlContains、urlMatches和urlPrefix是用于匹配网页URL的条件。

  • urlContains:表示URL包含指定的字符串时条件匹配。例如,如果指定urlContains: ‘example’,则只有当网页URL中包含"example"时条件才会匹配。
  • urlMatches:表示URL匹配指定的正则表达式时条件匹配。例如,如果指定urlMatches: > ‘https://www.example/*’,则只有当网页URL以"https://www.example/"开头时条件才会匹配。
  • urlPrefix:表示URL以指定的字符串开头时条件匹配。例如,如果指定urlPrefix: ‘https://example’,则只有当网页URL以"https://example"开头时条件才会匹配。

本文标签: 插件浏览器消息通信教程