Chrome插件开发实战指南:从Manifest V3到本地加载与权限排错

本文用一个最小的“页面标记器”示例,演示 Chrome 扩展从目录结构、manifest.json、弹窗脚本到本地加载和调试的完整链路。重点不是堆功能,而是先建立一套可以定位问题的开发骨架。

一、先明确:Chrome插件不是普通网页

Chrome 插件由浏览器管理,代码运行在不同上下文中:

  • 扩展页面:例如弹窗、设置页,可以使用部分 chrome.* API;
  • 内容脚本:运行在网页上下文附近,用于读取或修改页面内容;
  • 扩展 Service Worker:在 Manifest V3 中承担事件处理任务,但不会直接访问网页 DOM;
  • 清单文件manifest.json 声明名称、版本、入口、权限和资源。

如果把它当成“打开一个 HTML 文件”,常见结果就是:页面能显示,但权限、脚本注入和调试方式全部混乱。开发时应该先确定每段代码属于哪个上下文,再决定使用哪一个 API。
在这里插入图片描述

二、这次实战要做什么

本例做一个尽量小的扩展:点击扩展图标打开弹窗,再点击按钮给当前网页添加一个固定的“已标记”提示条;再次点击则移除提示条。

这个例子可以练习四个关键点:

  1. 用 Manifest V3 配置扩展入口;
  2. activeTab 获得用户操作后的临时当前页访问能力;
  3. scripting.executeScript() 向当前标签页注入函数;
  4. chrome://extensions 加载未打包扩展并查看错误。

示例不请求全站访问权限,也不读取网页表单、Cookie或用户账号信息。这样做的目的,是让权限范围和功能范围保持一致。
在这里插入图片描述

三、测试环境与资料边界

  • 测试日期:2026-09-18
  • 运行环境:Windows + Chrome 浏览器开发者模式
  • 扩展规范:Manifest V3
  • 示例类型:本地未打包扩展
  • 资料来源:Chrome for Developers 官方扩展文档
  • 端到端状态:本文文件结构和代码已做静态检查,未在本机真实 Chrome 窗口中完成点击加载验证

Chrome 扩展的权限、接口和商店要求可能随浏览器版本和官方文档变化。发布前应重新核对当前文档,不要把本文的示例版本号当成生产版本策略。

四、创建项目目录

先创建一个空目录,例如:

chrome-page-marker/
├─ manifest.json
├─ popup.html
└─ popup.js

manifest.json 必须位于扩展目录的根目录。目录名可以自定义,但不要把扩展放在包含密钥、客户资料或其他不应被工具读取的目录中。
在这里插入图片描述

五、编写 Manifest V3 清单

新建 manifest.json

{
  "manifest_version": 3,
  "name": "页面标记器",
  "version": "1.0.0",
  "description": "点击按钮后给当前页面添加或移除标记提示条。",
  "permissions": [
    "activeTab",
    "scripting"
  ],
  "action": {
    "default_popup": "popup.html"
  }
}

这里有三个容易写错的点:

  • manifest_version 使用 3
  • action.default_popup 指向真实存在的 HTML 文件;
  • activeTabscripting 是本示例所需权限,不要为了“以后可能用到”而额外申请 tabscookies 或全站主机权限。

activeTab 的思路是:用户主动点击扩展后,扩展获得当前标签页的临时访问能力。它不等于永久访问所有网站。权限越少,用户看到的警告和潜在影响通常越容易解释。

六、编写弹窗页面

新建 popup.html

<!doctype html>
<html lang="zh-CN">
<head>
  <meta charset="utf-8">
  <title>页面标记器</title>
  <style>
    body {
      width: 220px;
      margin: 0;
      padding: 16px;
      font-family: system-ui, sans-serif;
    }

    button {
      width: 100%;
      padding: 8px 12px;
      cursor: pointer;
    }

    #status {
      min-height: 20px;
      margin-top: 10px;
      color: #475569;
      font-size: 12px;
    }
  </style>
</head>
<body>
  <button id="mark">切换页面标记</button>
  <div id="status" role="status"></div>
  <script src="popup.js"></script>
</body>
</html>

弹窗只负责用户操作和状态显示,不把复杂的页面逻辑直接写在 HTML 的内联事件里。这样更容易定位脚本错误,也方便以后拆分功能。
在这里插入图片描述

七、编写注入脚本

新建 popup.js

const button = document.querySelector("#mark");
const status = document.querySelector("#status");

button.addEventListener("click", async () => {
  status.textContent = "正在处理当前页面……";

  try {
    const [tab] = await chrome.tabs.query({
      active: true,
      lastFocusedWindow: true,
    });

    if (!tab || typeof tab.id !== "number") {
      throw new Error("没有找到当前标签页");
    }

    await chrome.scripting.executeScript({
      target: { tabId: tab.id },
      func: () => {
        const id = "local-page-marker";
        const oldMarker = document.getElementById(id);

        if (oldMarker) {
          oldMarker.remove();
          return "已移除页面标记";
        }

        const marker = document.createElement("div");
        marker.id = id;
        marker.textContent = "当前页面已标记";
        Object.assign(marker.style, {
          position: "fixed",
          top: "12px",
          right: "12px",
          zIndex: "2147483647",
          padding: "8px 12px",
          color: "#ffffff",
          background: "#2563eb",
          borderRadius: "6px",
          font: "14px/1.4 system-ui, sans-serif",
        });
        document.documentElement.appendChild(marker);
        return "已添加页面标记";
      },
    });

    status.textContent = "操作完成";
  } catch (error) {
    console.error(error);
    status.textContent = "当前页面无法注入脚本,请查看扩展错误信息";
  }
});

这里的 func 会被注入当前标签页执行。它只创建或移除一个带固定 ID 的 DOM 元素,不读取页面正文,也不把页面内容发送到外部服务。

需要注意:chrome:// 页面、扩展商店页面以及部分受限制的页面不一定允许普通扩展脚本注入。遇到“代码没反应”时,先确认是不是页面类型限制,不要马上判断为权限配置完全错误。
在这里插入图片描述

八、在 Chrome 中加载未打包扩展

  1. 打开新标签页,输入 chrome://extensions
  2. 打开右上角的“开发者模式”;
  3. 点击“加载已解压的扩展程序”;
  4. 选择包含 manifest.json 的项目根目录;
  5. 加载成功后,打开一个普通网页;
  6. 点击扩展图标,再点击“切换页面标记”。

如果页面出现蓝色提示条,说明这个最小流程已经走通。修改 manifest.jsonpopup.js 后,回到扩展管理页点击重新加载,再重新打开弹窗测试。

九、出现错误时先看哪里

1. 扩展无法加载

优先检查:

  • manifest.json 是否是合法 JSON;
  • 是否多了尾逗号;
  • default_popup 的文件名是否拼写一致;
  • 选择的目录是否真的包含 manifest.json

不要只看浏览器弹出的概括性提示。扩展管理页通常会提供具体错误信息,先复制错误文本,再定位对应文件和行号。

2. 点击按钮没有效果

依次排查:

  1. 弹窗是否正常打开;
  2. popup.js 是否加载;
  3. 当前页面是否为受限制页面;
  4. scripting 是否写进 permissions
  5. tab.id 是否存在;
  6. 执行注入时是否抛出异常。

可以右键扩展弹窗,选择“检查”,在弹出的 DevTools Console 中查看 console.error 和异常堆栈。

3. 修改代码后看不到变化

不同扩展组件的重新加载要求不同。清单文件、Service Worker 和内容脚本的变化,通常需要在扩展管理页重新加载扩展;弹窗 HTML 的小修改可能在重新打开弹窗后生效,但为了避免缓存或上下文混淆,开发阶段建议形成固定动作:保存文件、重新加载扩展、关闭并重新打开弹窗、再测试目标页面。

十、什么时候需要 Service Worker

本示例没有使用后台事件,所以不必为了“看起来完整”强行加入 Service Worker。只有当你需要监听安装事件、右键菜单、通知、标签页事件或其他后台事件时,才在 manifest.json 中声明:

{
  "background": {
    "service_worker": "service-worker.js"
  }
}

Manifest V3 中,扩展 Service Worker 会在需要时加载,空闲时可能被停止;它不能直接访问 DOM。如果需要操作网页,应使用内容脚本、脚本注入或其他合适的扩展页面通信机制。

另一个容易踩坑的点是:不要把远程 JavaScript 地址当作 Service Worker 或扩展代码来源。扩展代码应随扩展包一起提供,具体限制以当前官方文档为准。

十一、权限设计比功能堆叠更重要

Chrome 扩展开发不是权限越多越强。建议按下面的顺序设计:

  • 核心功能必须使用的权限,才放进 permissions
  • 可选功能需要的权限,考虑放进 optional_permissionsoptional_host_permissions
  • 能用 activeTab 解决的临时页面操作,不要直接申请全站主机权限;
  • 不使用的 tabscookieswebRequest 等权限不要提前申请;
  • 新增权限前,重新解释用户为什么需要它,以及会触发什么警告。

如果扩展需要访问 file:// 页面或无痕窗口,用户还需要在扩展详情页单独允许相应访问。这个开关不是写进代码后就自动获得的。

十二、上线前的最小检查清单

  • manifest.json 可以被 Chrome 正常识别;
  • 扩展名称、描述和版本号与实际功能一致;
  • 权限与功能一一对应,没有“预留权限”;
  • 普通网页可以完成一次添加和移除标记;
  • 受限制页面无法执行时,界面有可理解的提示;
  • 没有把密钥、Cookie、用户数据写入扩展包;
  • 修改清单、脚本和后台代码后知道如何重新加载;
  • 远程代码、第三方库和图标素材的来源已经确认;
  • 发布前重新查看 Chrome 官方文档和目标发布渠道要求。

十三、结论

一个能长期维护的 Chrome 插件,起点不是复杂功能,而是清晰的运行上下文、最小权限和可重复的调试流程。本文示例只做页面标记,却覆盖了 Manifest V3、本地加载、脚本注入、受限页面和权限排错这些最容易反复出问题的基础环节。

如果后续要扩展功能,建议每次只增加一个变量:先加一个按钮,再加一个 API,再加一个权限,并在每一步记录“为什么需要、在哪个上下文运行、如何验证、失败时看什么日志”。这比一次性搭建一个权限很多但无法解释的插件更容易维护。

文章底部技术标签

#Chrome插件 #ManifestV3 #JavaScript #浏览器扩展 #前端开发

参考资料

  1. Chrome for Developers:Hello World extension;核验日期:2026-09-18。
  2. Chrome for Developers:Declare permissions;核验日期:2026-09-18。
  3. Chrome for Developers:About extension service workers;核验日期:2026-09-18。
  4. Chrome for Developers:Extension service worker basics;核验日期:2026-09-18。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值