Skip to content

Chrome 扩展开发手册:从本地调试到商店发布

归类:开发工具 / Chrome Extension / Manifest V3 发生时间:2026-03-26 状态:✅ 已沉淀


一、背景

在本地封装浏览器扩展时,最容易出现一种错觉:

  1. chrome://extensions/ 里加载本地目录一切正常
  2. 打成 zip 上传到应用商店后,功能却异常
  3. 第一反应通常是“是不是权限不够”

但真实情况往往更细。

基于两个实际项目的经验:

  • 一个偏数据操作,重点使用 cookiestabsstorage
  • 一个偏页面交互,重点使用 contextMenusscriptingcontent-scriptweb_accessible_resources

可以抽象出一套更稳定的 Chrome 扩展开发方法,尤其适合 Manifest V3 场景。

二、推荐目录结构

两个项目虽然组织方式不同,但都说明了一件事:运行时文件、构建脚本、发布产物最好分层。

1. 简单扩展可以平铺

适合文件少、逻辑集中、直接从根目录打包的项目。

text
extension/
├── manifest.json
├── background.js
├── popup.html
├── popup.js
├── style.css
├── icons/
├── _locales/
├── scripts/
└── dist/

这类结构的优点是上手快,适合功能明确、页面不多的扩展。

2. 稍复杂的扩展建议拆出 src/

适合同时维护源码、构建脚本、文档和发布产物的项目。

text
extension/
├── src/
│   ├── manifest.json
│   ├── background.js
│   ├── content-script.js
│   ├── popup.html
│   ├── popup.js
│   ├── vendor-lib.min.js
│   └── icons/
├── scripts/
├── docs/
├── dist/
└── package.json

这类结构更适合后续演进,因为源码、脚本和最终产物边界更清晰。

三、Manifest V3 的最小认知模型

开发扩展时,建议先把四种“能力边界”分清:

1. permissions

这里声明扩展可以使用哪些 Chrome API。

常见示例:

  • cookies
  • tabs
  • storage
  • activeTab
  • contextMenus
  • scripting

如果你要读写 Cookie,没有 cookies 权限就不行。 如果你要动态注入脚本,没有 scripting 权限就不行。

2. host_permissions

这里声明扩展能作用于哪些站点。

如果内容脚本、Cookie 操作、接口访问依赖具体域名,就要把对应站点声明进去。

推荐做法:

  • 调试期可以先用 <all_urls>*://*/*
  • 收敛发布时尽量最小化范围
  • 第三方接口域名和页面作用域分开考虑

3. content_scripts

适合“进入页面就要存在”的逻辑,例如:

  • 基础样式注入
  • 页面监听
  • DOM 操作能力

如果逻辑不是全量常驻,而是用户触发后再运行,更适合放到 chrome.scripting.executeScript()

4. web_accessible_resources

这项最容易被忽略。

当页面中的脚本、样式或资源需要通过 chrome.runtime.getURL() 暴露给页面环境时,对应文件必须出现在 web_accessible_resources 中。

典型场景:

  • 动态加载第三方库
  • 暴露样式文件
  • 页面侧脚本访问扩展内静态资源

四、推荐的运行架构

从两个项目抽象下来,一个更稳的 MV3 组织方式如下:

1. background/service_worker

负责:

  • 右键菜单注册
  • 标签页消息分发
  • 权限相关 API 调用
  • 触发脚本注入

2. content-script

负责:

  • 页面 DOM 注入
  • 面板渲染
  • 用户交互反馈
  • 页面内识别、采集、展示逻辑

3. popup

负责:

  • 工具栏入口
  • 轻量级配置
  • 向后台脚本发起操作请求

4. 第三方静态库

负责:

  • 二维码生成库
  • 二维码识别库
  • 哈希或加密库

这些库不要和业务脚本混写,最好独立成可枚举、可检查、可复制的静态文件。

五、构建与发布的推荐流程

1. 版本号必须双写同步

实际项目里最常见的是:

  • package.json 有版本
  • manifest.json 也有版本

如果两边不同步,扩展面板、发布包、商店版本号很容易不一致。

推荐做法:

  1. package.json 为主版本源
  2. 构建时自动回写 manifest.json
  3. 打包前再次校验版本一致

2. 发布包不要直接依赖源码目录“刚好齐全”

更稳妥的做法是维护明确的打包清单,包括:

  • 必需文件
  • 可选文件
  • 需要整目录复制的资源目录
  • 需要排除二次压缩的第三方库

如果没有清单,后期加一个 jsQR.min.jsmd5.min.js 这类文件时,很容易出现:

  • 本地源码里有
  • 调试加载目录时能用
  • 发布 zip 里漏掉
  • 商店安装后运行时报 Could not load file

3. 构建脚本要分“完整发布”和“仅打包”

推荐拆成两种模式:

  • 完整发布:检查 git、升级版本、打 tag、生成压缩包
  • 仅打包:使用当前版本直接出 zip,用于快速验证和提审

这样做的好处是:

  • 脏工作区不会阻塞所有打包动作
  • 验证发布包时不必强制走完整发布流程
  • 可以快速复现“商店包问题”而不引入额外变量

4. 生成产物后一定要做内容校验

建议至少检查三件事:

  1. manifest.json 是否存在且版本正确
  2. 关键静态文件是否被打进 zip
  3. 资源目录和图标路径是否完整

可以直接做这种检查:

bash
unzip -l dist/your-extension-vx.y.z.zip

六、最容易踩坑的几个问题

1. 本地可用,商店包不可用

这是最值得记的一类问题。

很多时候不是权限问题,而是发布包漏资源。

典型表现:

  • 本地“加载已解压扩展程序”可用
  • 商店安装版本报错
  • 控制台出现 Could not load file: 'xxx.min.js'

优先排查顺序建议是:

  1. zip 里是否真的包含该文件
  2. 打包清单是否遗漏
  3. 文件名大小写是否一致
  4. 是否需要加入 web_accessible_resources
  5. 最后再看权限声明

2. 动态注入脚本失败

如果使用:

js
chrome.scripting.executeScript({
  target: { tabId },
  files: ['jsQR.min.js']
})

那么至少要满足:

  1. 文件真实存在于最终扩展包中
  2. 路径相对于扩展根目录正确
  3. 已声明 scripting 权限
  4. 目标页面允许注入

3. 图标路径在开发和发布阶段不一致

有些项目会同时存在 icons/images/ 两套目录。

这类场景如果不封装脚本,很容易出现:

  • 本地能显示图标
  • 某个阶段图标路径被替换
  • 构建结束后忘了恢复

推荐做法是:

  1. 预构建时替换路径
  2. 构建结束后恢复 manifest
  3. 不要手工来回改 JSON

4. 构建脚本被脏工作区卡住

“检查 git 是否干净”适合正式发布,但不适合所有场景。

更稳的实践是保留一个仅打包命令,用于:

  • 商店包复现
  • 资源补包验证
  • 非发布态产物检查

5. macOS 垃圾文件被打进发布包

例如:

  • .DS_Store

虽然通常不影响运行,但会污染发布包内容,也会让排查产物时多出噪音。

建议在打包前清理一次生成目录。

七、适合团队复用的排查清单

当扩展出现功能异常时,可以先按下面顺序排查:

  1. manifest.jsonpermissions 是否覆盖所需 API
  2. host_permissions 是否覆盖目标站点
  3. content_scriptsscripting.executeScript 的职责是否混乱
  4. 通过 chrome.runtime.getURL() 访问的资源是否加入 web_accessible_resources
  5. 最终 zip 是否真的包含对应静态文件
  6. 版本号是否和提审版本一致
  7. 图标、样式、国际化资源目录是否完整复制

八、推荐的最小发布自检

在提审前,建议固定做一次:

bash
# 1. 生成发布包
npm run build

# 或者只打包验证
npm run build:pack

# 2. 检查 zip 内容
unzip -l dist/*.zip

# 3. 用发布目录重新加载扩展做最终验证
# chrome://extensions/

如果项目已经有自定义脚本,也可以直接调用底层 shell 脚本,但最好保证:

  • 有明确的发布目录
  • 有稳定的 zip 命名规则
  • 有关键资源存在性检查

九、结论

Chrome 扩展开发最难的部分,通常不是“能不能把功能写出来”,而是“本地调试、目录组织、权限声明、资源打包、商店提审”能不能形成一条稳定链路。

基于这两个项目的经验,可以优先坚持三条原则:

  1. 把运行时资源清单化,不依赖“目录里刚好有”
  2. 把发布流程脚本化,不依赖手工同步版本和资源
  3. 把问题定位顺序标准化,先查产物,再查路径,再查权限

这样做之后,扩展从本地调试走到商店发布会稳定很多,排查效率也会明显提升。