如何在 Chrome 扩展的 Popup Script 访问当前 Tab 页的 JS 变量

📌 如何在 Chrome 扩展的 Popup Script 访问当前 Tab 页的 JS 变量

🚨 关键点

  1. Popup Script 不能直接访问网页变量,必须通过 Injected Script 读取 window 作用域的数据。
  2. Injected Script 不能直接与 Chrome 扩展通信,需要 Content Script 作为中间桥梁。
  3. 数据传递流程:
    • Popup Script 请求 Content Script 注入 Injected Script
    • Injected Script 读取网页变量并通过 window.postMessage() 发送给 Content Script
    • Content Script 监听 window.postMessage() 并通过 chrome.runtime.sendMessage() 发送给 Popup Script
    • Popup Script 监听消息并获取网页变量值。

🚀 实现步骤

1️⃣ Injected Script(获取网页变量)

// injected.js
(function() {
    let variableValue = window.someVariable; // 访问网页中的 JS 变量
    window.postMessage({ type: "FROM_PAGE", data: variableValue }, "*"); // 发送给 Content Script
})();

2️⃣ Content Script(注入 Injected Script + 监听消息)

// content.js
// 监听 Injected Script 发送的消息
window.addEventListener("message", (event) => {
    if (event.source === window && event.data.type === "FROM_PAGE") {
        chrome.runtime.sendMessage({ action: "pageData", data: event.data.data }); // 发送给 Popup Script
    }
});

// 监听 Popup Script 的请求,并注入 Injected Script
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
    if (request.action === "injectScript") {
        const script = document.createElement("script");
        script.src = chrome.runtime.getURL("injected.js");
        (document.head || document.documentElement).appendChild(script);
        script.remove();
    }
});

3️⃣ Popup Script(请求变量 & 接收数据)

// popup.js
document.getElementById("fetchVariableBtn").addEventListener("click", async () => {
    let [tab] = await chrome.tabs.query({ active: true, currentWindow: true });

    chrome.tabs.sendMessage(tab.id, { action: "injectScript" });

    chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
        if (message.action === "pageData") {
            alert("网页变量值:" + message.data);
        }
    });
});

4️⃣ manifest.json 需要的权限

{
    "manifest_version": 3,
    "permissions": ["tabs"],
    "host_permissions": ["<all_urls>"],
    "content_scripts": [
        {
            "matches": ["<all_urls>"],
            "js": ["content.js"]
        }
    ]
}

📌 数据流

[Popup Script] → (chrome.tabs.sendMessage) → [Content Script]
[Content Script] → (注入 Injected Script) → [网页环境]
[Injected Script] → (window.postMessage) → [Content Script]
[Content Script] → (chrome.runtime.sendMessage) → [Popup Script]

🔥 总结

Popup Script 不能直接访问网页变量,必须通过 Injected Script。
Injected Script 不能直接通信,需要 Content Script 作为桥梁。
最终 Popup Script 获取网页变量 需要 3 步:注入 Injected Script → 监听 window.postMessage() → 发送数据回 Popup Script

这样,Popup Script 就能访问网页中的 JS 变量了!🚀

您说:

sidepanel 是否也是一样的?

ChatGPT 说:

是的,Sidepanel(侧边栏)与 Popup 在许多方面的行为和限制是相似的。侧边栏也是 Chrome 扩展的一部分,可以显示 HTML 内容并与用户交互。因此,Sidepanel Script 也无法直接访问当前 Tab 页的 JavaScript 变量,需要采用与 Popup 相似的方式来访问网页中的数据。

📌 Sidepanel 与 Popup 的相似点

  • SidepanelPopup 都是扩展的界面,用户通过点击扩展图标或在 UI 中激活时才会出现。
  • Sidepanel 运行在扩展的环境中,无法直接访问当前 Tab 页的 window 对象中的 JS 变量。
  • 与 Popup 一样,Sidepanel 需要通过 Injected Script 来访问网页的数据,并通过 Content ScriptBackground Script 将数据传回。

🚀 解决方案

  • Sidepanel Script 的实现方法与 Popup Script 完全相同,使用 Injected Script 来访问网页的 JS 变量,经过 Content Script 中转,然后通过消息传递将数据返回给 Sidepanel

📌 具体步骤

1️⃣ Injected Script(读取网页变量)

// injected.js
(function() {
    let variableValue = window.someVariable; // 访问网页中的 JS 变量
    window.postMessage({ type: "FROM_PAGE", data: variableValue }, "*"); // 发送给 Content Script
})();

2️⃣ Content Script(注入 Injected Script + 监听消息)

// content.js
// 监听 Injected Script 发送的消息
window.addEventListener("message", (event) => {
    if (event.source === window && event.data.type === "FROM_PAGE") {
        chrome.runtime.sendMessage({ action: "pageData", data: event.data.data }); // 发送给 Sidepanel Script
    }
});

// 监听 Sidepanel Script 的请求,并注入 Injected Script
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
    if (request.action === "injectScript") {
        const script = document.createElement("script");
        script.src = chrome.runtime.getURL("injected.js");
        (document.head || document.documentElement).appendChild(script);
        script.remove();
    }
});

3️⃣ Sidepanel Script(请求变量 & 接收数据)

// sidepanel.js
document.getElementById("fetchVariableBtn").addEventListener("click", async () => {
    let [tab] = await chrome.tabs.query({ active: true, currentWindow: true });

    chrome.tabs.sendMessage(tab.id, { action: "injectScript" });

    chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
        if (message.action === "pageData") {
            alert("网页变量值:" + message.data);
        }
    });
});

4️⃣ manifest.json 需要的权限

{
    "manifest_version": 3,
    "permissions": ["tabs"],
    "host_permissions": ["<all_urls>"],
    "content_scripts": [
        {
            "matches": ["<all_urls>"],
            "js": ["content.js"]
        }
    ],
    "side_panel": {
        "default_path": "sidepanel.html"
    }
}

📌 数据流

[Sidepanel Script] → (chrome.tabs.sendMessage) → [Content Script]
[Content Script] → (注入 Injected Script) → [网页环境]
[Injected Script] → (window.postMessage) → [Content Script]
[Content Script] → (chrome.runtime.sendMessage) → [Sidepanel Script]

🚀 总结

  • Sidepanel 与 Popup 在访问当前 Tab 页的 JavaScript 变量时的处理方式相同:都需要通过 Injected ScriptContent Script 中转。
  • Sidepanel 不能直接访问 Tab 页的 JS 变量,因此需要依赖消息传递机制和注入脚本来获取网页中的数据。

如果你打算在 Sidepanel 中实现类似功能,可以按照与 Popup 相同的方式进行处理。💡

您说:

是否意味着 popup 和 sidepanel 都是单实例的,不存在每一个页面一个 popup 和 sidepanel?

ChatGPT 说:

是的,PopupSidepanel 都是单实例的,不会为每一个页面分别创建一个新的实例。它们是扩展的界面组件,通常是 全局唯一的,并且 在扩展的生命周期内只有一个实例

📌 详细解释:

  • Popup:
  • 单实例:Popup 是在用户点击扩展图标时显示的,它是全局唯一的。在一个浏览器窗口或标签页中,只有一个 Popup 存在。
  • 每个 Tab 页共享同一个 Popup:Popup 的生命周期与扩展图标的点击相关,而不是与具体的 Tab 页绑定。因此,当你点击扩展图标时, 所有 Tab 页都使用同一个 Popup 实例。
  • Sidepanel:
  • 单实例:Sidepanel 是另一种全局唯一的 UI 组件,可以通过浏览器的侧边栏显示。每次用户打开侧边栏时,只有一个实例被加载。
  • 每个 Tab 页共享同一个 Sidepanel:不同于 Popup,Sidepanel 是独立的界面组件,不会为每个 Tab 页创建一个新的 Sidepanel。它也只存在一个实例,并与当前的 Tab 页同步。

📌 不同页面与同一个 Popup 或 Sidepanel 实例:

  • Popup:当你点击扩展图标时,Popup 会弹出,并且 它会共享所有 Tab 页的状态和变量。你不能为每个 Tab 页创建不同的 Popup。换句话说,所有 Tab 页的 Popup 界面是共用一个实例。
  • Sidepanel:类似地,Sidepanel 也只有一个实例。它通常是固定的,显示在浏览器的侧边栏中,适用于所有 Tab 页。无论你切换哪个 Tab 页, 只有一个 Sidepanel 显示在侧边栏。

总结:

  • Popup 和 Sidepanel 都是单实例的,意味着在一个浏览器窗口或标签页中,只会有一个 Popup 或 Sidepanel 实例
  • 如果你需要为不同的 Tab 页提供个性化的界面,Popup 和 Sidepanel 可能不是最佳选择,因为它们会共享状态。

因此,PopupSidepanel 更适用于全局性的信息显示和交互,而如果你需要为每个 Tab 页提供独立的界面或内容,可能需要通过其他方式来实现,比如使用 Content ScriptBackground Script 来管理每个 Tab 页的状态。

injected script 是多实例的,被 content script 根据不同的规则注入到当前 chrome 页面。popup 和 background.js 都是单实例的。

站在 backgroud.js 的角度, content.js 是多例的?

ChatGPT 说:

是的,从 background.js 的角度来看,content.js多例的

解释:

  • content.js 是由浏览器根据每个标签页(Tab)注入的。每个符合注入条件的页面(Tab)都会有独立的 content.js 实例。
  • 因此,每个 tab 页面都会有一个单独的 content.js 实例,并且这些实例是相互独立的。

举个例子:

假设你在 manifest.json 中配置了 content.js 需要注入到所有 https://www.example.com/* 页面:

{
  "manifest_version": 3,
  "content_scripts": [
    {
      "matches": ["https://www.example.com/*"],
      "js": ["content.js"]
    }
  ]
}

当用户在浏览器中打开多个 https://www.example.com/* 页面时,浏览器会为每个页面 注入一个独立的 content.js 实例。这些 content.js 实例之间相互独立,它们分别在各自的 Tab 中执行,维护各自的状态和变量。

background.js 角度看:

  • 多实例:对于每个符合条件的页面(Tab),都会有一个独立的 content.js 实例。因此,background.js 会看到多个 content.js 实例,这些实例对应不同的 Tab 页面。
  • background.js 通过 chrome.tabs API 可以管理和获取每个 Tab 页的状态或与其进行通信,而每个 Tab 页上的 content.js 实例则相互独立。

如何在 background.js 中管理多实例:

background.js 可以通过 chrome.tabs API 获取每个 Tab 的信息,并可以与每个 Tab 中的 content.js 进行通信。例如,可以通过 chrome.tabs.sendMessage 向特定的 Tab 发送消息,或者通过 chrome.runtime.onMessage 来监听来自不同 Tab 的消息。

示例:background.js 监听多个 content.js 实例的消息

chrome.runtime.onMessage.addListener(function(message, sender, sendResponse) {
    if (sender.tab) {
        // 这里的 sender.tab 是包含 content.js 的 Tab 信息
        console.log("来自 Tab " + sender.tab.id + " 的消息: ", message);
    }
});

// 向 Tab 发送消息
chrome.tabs.query({}, function(tabs) {
    tabs.forEach(function(tab) {
        chrome.tabs.sendMessage(tab.id, { action: "getData" }, function(response) {
            console.log("Tab " + tab.id + " 的数据: ", response);
        });
    });
});

总结:

  • content.js多实例 的,每个 Tab 页面有一个独立的 content.js 实例。
  • background.js 的角度看,它管理着所有这些独立的 content.js 实例,可以通过 chrome.tabs API消息传递机制 与它们进行交互。

Chrome 扩展页面的生命周期总结:

  1. Popup (popup.html)
  • 生命周期:每次打开时重新加载,关闭后销毁。

  • 特点:每次用户点击扩展图标时,都会重新创建并加载 popup。每次打开时页面会刷新,适合用来展示简短信息或简单交互。

  • 优化建议:为了提高性能,最好使用轻量级的前端框架(例如 Vanilla JSPreact),避免过度消耗资源。

    1. Sidepanel (sidepanel.html)
  • 生命周期:一旦打开,除非用户主动关闭或隐藏,否则保持打开状态,不会重新加载。

  • 特点:与 popup 不同,sidepanel 会持续存在,直到用户关闭或隐藏它。适合用于构建复杂、持久的界面,并可以使用像 React 这样的较重前端框架来管理组件和状态。

  • 优化建议:由于其持续存在,可以使用更重的前端框架(如 ReactVue)来处理复杂的 UI 和交互。

    1. Options Page (options.html)
  • 生命周期:每次打开时重新加载,关闭后销毁。

  • 特点options.html 是一个独立的设置页面,用户通过扩展菜单或设置进入。每次打开时,页面的内容会重新加载并初始化。它通常用于显示和编辑扩展的配置设置。

  • 优化建议:根据需求选择合适的框架,轻量级框架或者纯 HTML/JavaScript 即可满足大多数配置页面的需求。

总结:

  • PopupOptions Page 都是 临时加载的页面,每次打开时会重新加载并刷新内容,生命周期较短。
  • Sidepanel 是一个 持久性界面,它会保持打开直到用户手动关闭或隐藏,因此它适合使用更复杂的前端框架(如 React)。
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇