admin管理员组

文章数量:1130349

前言

前面我们介绍了Chrome插件中存在的4种JS,那么它们之间如何互相通信呢?

1. Chrome插件的5种类型的JS对比

JS种类content scriptservice workerpopupdevtools
可访问的API只能访问dom、i18n、storage、runtime等部分API可访问绝大部分API,除了devtools系列可访问绝大部分API,除了devtools系列只能访问 devtools、runtime等部分API
DOM访问情况页面DOM弹窗DOM页面DOM
生命周期随页面加载/卸载按需唤醒,最长 5 分钟(和V2版本插件有区别)随弹窗打开/关闭随 DevTools 打开/关闭
典型用途修改页面内容、监控用户交互跨标签管理、定时任务、消息路由快速操作入口、状态展示开发者工具集成、调试工具开发

2. 消息通信

2.1 popup.js和service-worker.js

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

  • 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返回的数据
});

  • service-worker.jspopup.js 发送消息(因为 popup.js 的生命周期随弹窗打开才开始,所以演示代码里, service-worker.js 加了延时发消息)
// popup.js
chrome.runtime.onMessage.addListener(function (message, sender, sendResponse) {
  if (message.type === 'serviceWorkerSendMessageToPopup') {
    sendResponse({ success: true, message: 'popup.js收到消息' });
  }
});

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

2.2 content.js和service-worker.js

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

  • 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);
});

  • service-worker.jscontent.js 发送消息(演示代码里 service-worker.js 延时发送消息了,实际使用时,根据实际情况写代码)
//manifest.json 如需使用 tabs API,请在扩展程序manifest中声明 "tabs" 权限。
{
  "permissions": [
    "tabs"
  ]
}

// service-worker.js
setTimeout(function () {
    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返回的数据
        })
    })
}, 5000);

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

2.3 content.js和popup.js

参考2.2章节写代码即可,使用方法一样。
注意事项:

  • content_scriptspopup 主动发消息的前提是 popup 必须打开!否则需要利用 background 作中转。
  • 如果 backgroundpopup 同时监听,那么它们都可以同时收到消息,但是只有一个可以 sendResponse ,一个先发送了,那么另外一个再发送就无效。

2.4 通过 chrome.storage 共享数据

有时候你可能需要在多个部分之间共享数据,chrome.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)

2.5 使用 chrome.runtime.connect 和长连接

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

  • 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);
});

  • 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: '我是容易摔倒的猪!'});
        });
    }
});


注意事项:

  • 连接关闭:连接没有显式关闭时会一直保持,所以要确保在合适的时机断开连接,避免内存泄漏。
  • 服务工作线程:在V3版本插件中,背景脚本使用 service worker,它是惰性加载的,意味着它并不是一直在运行,所以要注意如何管理长连接的生命周期。

要实现三方长连接,我们可以使用以下方式:

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

前言

前面我们介绍了Chrome插件中存在的4种JS,那么它们之间如何互相通信呢?

1. Chrome插件的5种类型的JS对比

JS种类content scriptservice workerpopupdevtools
可访问的API只能访问dom、i18n、storage、runtime等部分API可访问绝大部分API,除了devtools系列可访问绝大部分API,除了devtools系列只能访问 devtools、runtime等部分API
DOM访问情况页面DOM弹窗DOM页面DOM
生命周期随页面加载/卸载按需唤醒,最长 5 分钟(和V2版本插件有区别)随弹窗打开/关闭随 DevTools 打开/关闭
典型用途修改页面内容、监控用户交互跨标签管理、定时任务、消息路由快速操作入口、状态展示开发者工具集成、调试工具开发

2. 消息通信

2.1 popup.js和service-worker.js

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

  • 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返回的数据
});

  • service-worker.jspopup.js 发送消息(因为 popup.js 的生命周期随弹窗打开才开始,所以演示代码里, service-worker.js 加了延时发消息)
// popup.js
chrome.runtime.onMessage.addListener(function (message, sender, sendResponse) {
  if (message.type === 'serviceWorkerSendMessageToPopup') {
    sendResponse({ success: true, message: 'popup.js收到消息' });
  }
});

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

2.2 content.js和service-worker.js

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

  • 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);
});

  • service-worker.jscontent.js 发送消息(演示代码里 service-worker.js 延时发送消息了,实际使用时,根据实际情况写代码)
//manifest.json 如需使用 tabs API,请在扩展程序manifest中声明 "tabs" 权限。
{
  "permissions": [
    "tabs"
  ]
}

// service-worker.js
setTimeout(function () {
    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返回的数据
        })
    })
}, 5000);

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

2.3 content.js和popup.js

参考2.2章节写代码即可,使用方法一样。
注意事项:

  • content_scriptspopup 主动发消息的前提是 popup 必须打开!否则需要利用 background 作中转。
  • 如果 backgroundpopup 同时监听,那么它们都可以同时收到消息,但是只有一个可以 sendResponse ,一个先发送了,那么另外一个再发送就无效。

2.4 通过 chrome.storage 共享数据

有时候你可能需要在多个部分之间共享数据,chrome.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)

2.5 使用 chrome.runtime.connect 和长连接

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

  • 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);
});

  • 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: '我是容易摔倒的猪!'});
        });
    }
});


注意事项:

  • 连接关闭:连接没有显式关闭时会一直保持,所以要确保在合适的时机断开连接,避免内存泄漏。
  • 服务工作线程:在V3版本插件中,背景脚本使用 service worker,它是惰性加载的,意味着它并不是一直在运行,所以要注意如何管理长连接的生命周期。

要实现三方长连接,我们可以使用以下方式:

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

本文标签: 之路插件消息通信测试