浏览器插件开发-popup
popup就是弹出层的页面,一般是用于插件的配置 popup.html只能引用扩展包内的本地文件,不能用远程地址,默认不能内嵌脚本,内联事件 网络请求需要在 manifest.json 中配置 h

浏览器插件开发-popup

发布时间:2025-12-27 (2025-12-27)

popup就是弹出层的页面,一般是用于插件的配置

  1. popup.html只能引用扩展包内的本地文件,不能用远程地址,默认不能内嵌脚本,内联事件
  2. 网络请求需要在 manifest.json 中配置 host_permissions 权限
  3. 生命周期短,只有点击扩展图标时,popup 才会加载,点击其他地方,popup 会立即销毁,所以不要在 popup 中写长时任务(比如定时器、轮询),销毁后会直接停止,这类逻辑要放到service worker中
  4. popup 只能操作自己的 DOM,无法直接操作当前网页的 DOM(需要通过 content script 实现)

如果要看popup的打印,需要右击弹出框,然后点检查

popup.html配置

这里面就是标准的html

如果要使用vue,jquery这些, 必须把这些js文件,下载到本地,然后引入

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>popup</title>
    <script src="popup/jquery.js"></script>
</head>
<body style="width: 200px">
<script src="popup/popup.js"></script>
</body>
</html>

但是使用vue的话,要麻烦一点

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>popup</title>
</head>
<body style="width: 200px">
<div id="app"></div>


<script type="module" src="popup/vue.global.js"></script>
<script type="module" src="popup/popup.js"></script>
</body>
</html>
import { createApp, h, ref } from './vue.global.js';

// 用 h() 函数创建组件(避免运行时编译模板)
const App = {
    setup() {
        // 响应式数据(Composition API)
        const title = ref("Vue 插件 Popup (V3 安全版)");
        const count = ref(0);
        const increment = () => {
            count.value++;
        };

        // 返回渲染函数(核心:预编译,无模板编译)
        return () => h('div', [
            h('h3', title.value),
            h('p', `计数:${count.value}`),
            h('button', { onClick: increment }, '点击+1')
        ]);
    }
};

// 挂载应用
createApp(App).mount('#app');

一般来说,popup就是一个配置页面,也不会有特别复杂的配置页面,如果有的,一是可以用vite脚手架开发,二是在专门的配置页面去开发

options页面

在manifest.json中配置

"options_ui": {
  "page": "hello.html",
  "open_in_tab": true
},

右键插件图标,点击选项就可以进到options页面了

如果要实现点击浏览器插件图标,跳转到options页面这个操作,需要在service worker里面实现

一是配置minifest.json中要有action配置

  "action": {
    "default_icon": {
      "16": "icon.png",
      "32": "icon.png",
      "48": "icon.png",
      "64": "icon.png",
      "128": "icon.png"
    }
  },

然后在background.js中监听插件图标的点击事件

// 监听插件图标的点击事件
chrome.action.onClicked.addListener(() => {
  // 打开插件的options页面
  chrome.runtime.openOptionsPage().catch((error) => {
    // 异常处理:如果openOptionsPage失败,手动拼接URL打开
    console.error('打开选项页面失败:', error);
    const optionsUrl = chrome.runtime.getURL('options/options.html');
    chrome.tabs.create({ url: optionsUrl });
  });
});

openOptionsPage() 是打开插件配置的options页面,也可以用chrome.runtime.getURL获取插件本地html的路径,然后用chrome.tabs.create去打开

popup通信

与content script通信

有的时候我们需要实现,点击popup的时候,再去执行content script

// 等待DOM加载完成
document.addEventListener('DOMContentLoaded', () => {
    const sendBtn = document.getElementById('btn1');

    sendBtn.addEventListener('click', async () => {
        try {
            // 1. 获取当前激活的标签页(需要tabs权限)
            const [activeTab] = await chrome.tabs.query({
                active: true,
                currentWindow: true
            });

            if (!activeTab.id) {
                console.warn("无法获取当前标签页ID")
                return;
            }

            // 2. 向content script发送消息
            const response = await chrome.tabs.sendMessage(activeTab.id, {
                type: 'POPUP_MESSAGE',
                data: '这是来自Popup的消息',
                timestamp: new Date().getTime()
            });

            // 3. 接收content script的响应并提示
            console.log(`Content Script响应:${response.message}`)
        } catch (error) {
            // 常见错误:当前页面没有注入content script
            console.error(`Content Script响应:${error.message}`)
        }
    });
});

content script监听消息

// 监听来自Popup的消息
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
    console.log('收到来自Popup的消息:', message);

    // 判断消息类型并处理
    if (message.type === 'POPUP_MESSAGE') {
        // 模拟处理逻辑
        const replyMessage = `已收到消息:${message.data},当前页面标题是「${document.title}」`;

        // 向Popup发送响应(sendResponse必须同步调用,异步需返回true)
        sendResponse({
            success: true,
            message: replyMessage
        });
    }

    // 如果需要异步处理,需返回true(比如调用API后再回复)
    // return true;
});

与service worker通信

直接用chrome.runtime.sendMessage发消息就行

const sendBtn2 = document.getElementById('btn2');

sendBtn2.addEventListener('click', async () => {
    try {
        // 2. 向content script发送消息
        const response = await chrome.runtime.sendMessage( {
            type: 'POPUP_MESSAGE',
            data: {
                message: '你好,Service Worker!',
                time: new Date().toLocaleTimeString()
            }
        });
        // 3. 接收content script的响应并提示
        console.log("Service worker 响应", response)
    } catch (error) {
        // 常见错误:当前页面没有注入content script
        console.error("Service worker 响应失败", error)
    }
});

background使用chrome.runtime.onMessage.addListener去接收消息

chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
    console.log('Service Worker收到消息:', message);

    // 判断消息类型,处理不同逻辑
    if (message.type === 'POPUP_MESSAGE') {
        // 模拟处理逻辑(比如读取插件存储、调用API等)
        const replyContent = `已收到你的消息「${message.data.message}」,时间:${message.data.time}`;

        // 向popup发送响应(同步回复)
        sendResponse({
            success: true,
            reply: replyContent
        });
    }
})