freeCodeCamp.org

如何使用 Plasmo 开发 Chrome 扩展 [完整手册]

8.7内容质量
如何使用 Plasmo 开发 Chrome 扩展 [完整手册]

TL;DR · AI 摘要

Plasmo 可自动化配置 Chrome 扩展项目,支持 TypeScript 与 React,本教程教你从零构建并发布标签分组扩展。

核心要点

  • Plasmo 自动生成 manifest.json 并集成热重载,支持 TypeScript 和 React 开箱即用。
  • 通过 chrome.tabs 和 chrome.tabGroups API 实现自动按域名分组标签页功能。
  • 项目可通过 plasmo build 构建并上传至 Chrome Web Store 发布。

结构提纲

按章节快速跳转。

  1. §Plasmo 简介

    Plasmo 是一个现代浏览器扩展开发框架,能自动生成 manifest.json 并集成 TypeScript、React 和热重载功能。

  2. 开发一个可将浏览器标签按网站域名自动分组并着色的 Chrome 扩展。

  3. 掌握 Chrome 扩展的基本结构、核心 API 使用以及现代前端工具链集成方法。

  4. 需要 Node.js 18+、pnpm、Chrome 浏览器和代码编辑器即可开始开发。

  5. 运行 'npx create-plasmo' 命令即可生成带 TypeScript 和 React 支持的完整扩展项目结构。

  6. 使用 plasmo build 生成生产包,解压后可提交到 Chrome Web Store 完成发布。

思维导图

用一张图看清主题之间的关系。

查看大纲文本(无障碍 / 无 JS 友好)
  • Plasmo 开发 Chrome 扩展
    • 核心优势
      • 自动生成 manifest
      • TypeScript + React 支持
      • 热重载开发体验
    • 关键技术
      • chrome.tabs API
      • chrome.tabGroups API
      • chrome.runtime 消息通信
    • 开发流程
      • npx create-plasmo 初始化
      • 本地调试与实时更新
      • plasmo build 构建发布包

金句 / Highlights

值得收藏与分享的关键句。

  • Plasmo 能处理所有配置工作,一条命令即可搭建好 TypeScript 与 React 已就绪的可运行项目。

    What is Plasmo?

    ⬇︎ 下载 PNG𝕏 分享到 X
  • 只需单击一次,Tab Grouper 扩展就会为每个网站自动创建彩色分组,便于查找和管理标签页。

    What You Will Build

    ⬇︎ 下载 PNG𝕏 分享到 X
  • Plasmo 不会隐藏 Chrome 扩展的概念,你仍可直接使用 chrome.tabs、chrome.runtime 等原生 API。

    What is Plasmo?

    ⬇︎ 下载 PNG𝕏 分享到 X
#Plasmo#Chrome 扩展#TypeScript#React#浏览器 API
打开原文

标题:如何使用 Plasmo 开发 Chrome 扩展 [完整手册]

来源链接:https://www.freecodecamp.org/news/how-to-develop-chrome-extensions-using-plasmo-handbook/

发布时间:2026-05-11T20:11:25.776Z

Markdown 内容:

![图片 1:如何使用 Plasmo 开发 Chrome 扩展 [完整手册]](https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/e0d0bca4-a2e8-495a-9c1c-4f0b9ef52630.png) Chrome 扩展是轻量级工具,可以增强并个性化你的浏览体验,无论是管理密码、翻译网页,还是为你每天使用的网站添加全新功能。

已有数百万开发者向 Chrome 网上应用店发布扩展,而构建一个扩展的门槛比你想象中更低。

在本手册中,你将从零开始,使用 TypeScript、React 和 Plasmo 框架开发并发布一个 Chrome 扩展。Plasmo 是一个现代框架,能自动处理重复性的配置和初始化工作,让你专注于功能开发,而非样板代码。

在此过程中,你会接触到真实可用的 Chrome 扩展 API,这些 API 正是生产级扩展的核心:查询标签页、创建标签组,以及在扩展的不同部分之间传递消息。

最终,你将拥有一套可运行的代码,理解扩展的整体结构,并掌握将自己创意发布到 Chrome 网上应用店所需的全部知识。

目录

什么是 Plasmo?

Plasmo 是一个用于构建浏览器扩展的开源框架。你可以将其视为 Create React App 或 Next.js 的“扩展版”——专为 Chrome 扩展打造。

如果没有 Plasmo,你需要手动编写 manifest.json 文件,自行配置构建工具、TypeScript 和 React。而 Plasmo 能帮你完成所有这些工作。

只需一条命令,就能生成一个已配置好 TypeScript 和 React 的项目脚手架。它会读取你的 package.json 并自动生成 Chrome 所需的 manifest.json,你无需直接编辑该文件。

此外,在开发过程中,源文件的任何更改都会自动重新构建并在 Chrome 中实时刷新。开箱即用的类型安全支持,包括对 Chrome 自身 API 的类型定义。

Plasmo 并不会隐藏 Chrome 扩展的核心概念。你仍然可以直接使用 chrome.tabschrome.runtime 等 Chrome API。它只是帮你省去了繁琐的初始化步骤,让你能够立即投入开发。

你将构建什么

在本教程中,你将从零开始构建一个名为 Tab Grouper 的 Chrome 扩展。

这个扩展会根据标签页的网站域名,自动将你的浏览器标签分组整理。

图片 2:Tab Grouper 扩展示例动图,展示如何按域名将打开的标签自动分组并着色
图片 2:Tab Grouper 扩展示例动图,展示如何按域名将打开的标签自动分组并着色

示例场景

假设你打开了 20 个标签页:5 个来自 GitHub,4 个来自 YouTube,3 个来自 Stack Overflow,其余 8 个来自其他网站。

只需点击一次,Tab Grouper 扩展就会为每个网站自动创建彩色标签组,让你更轻松地查找和管理标签页。

你将学到什么

完成本教程后,你将在三个方面获得实战经验。

第一,Chrome 扩展基础:了解扩展的底层工作原理、扩展的组成部分(清单文件、后台脚本、弹出窗口),以及如何在开发期间加载和测试扩展。

第二,Chrome API:特别是使用 chrome.tabs 管理浏览器标签页,使用 chrome.tabGroups 创建和自定义标签组,以及使用 chrome.runtime 在扩展的不同部分之间传递消息。

第三,现代 Web 开发工具链:使用 TypeScript 实现类型安全的 JavaScript,使用 React 构建弹出界面,以及使用 Plasmo 框架整合整个项目。

前提条件

你不需要成为以下任一技术的专家,但如果你熟悉基本的 JavaScript 或 TypeScript,并对 HTML 和 CSS 有大致了解,学习过程会更加顺畅。

对 React 有一定了解会有帮助,但不是必需的。我们将要构建的弹窗组件足够简单,即使你是 React 新手也能轻松跟上。

在软件方面,你需要安装 Node.js 18 或更高版本(点击此处下载)、Google Chrome 浏览器、一个代码编辑器(推荐使用 VS Code),以及 pnpm 作为包管理器。

验证你的环境

打开终端并运行以下命令,确认所有工具均已正确安装:

code
node --version
# 应输出 v18.0.0 或更高版本

npm --version
# 应输出 9.0.0 或更高版本

获取帮助

如果你遇到问题,可以查看仓库中的完整代码,查阅 Chrome 扩展官方文档,或在社区论坛中寻求帮助。

准备好了吗?

接下来,你将设置开发环境,并创建你的第一个 Chrome 扩展项目。

让我们开始吧!

项目设置

在本节中,你将使用 Plasmo 脚手架工具创建 Chrome 扩展项目,然后针对 Tab Grouper 进行定制。

你无需手动创建文件,而是让 Plasmo 自动生成一个包含所有必要配置的初始项目,再探索其结构并根据需求进行修改。

第一步:安装 pnpm(推荐)

Plasmo 官方推荐使用 pnpm,以实现更快的安装速度和更优的磁盘空间利用。请先检查是否已安装:

code
pnpm --version

如果看到版本号,跳转到第二步即可。

图片 3:终端中运行 pnpm --version 后显示版本号的输出结果
图片 3:终端中运行 pnpm --version 后显示版本号的输出结果

如果提示“命令未找到”,请使用以下命令安装:

code
npm install -g pnpm

第二步:创建你的扩展项目

运行以下命令创建一个新的 Plasmo 项目:

code
pnpm create plasmo tab-grouper
code
🟣 创建一个新的 Plasmo 扩展
📁 项目名称:tab-grouper
? 扩展描述:(为你的扩展提供一个不错的描述)
? 作者姓名:(你的名字)

Plasmo 将会自动搭建项目并安装依赖项。你可能会被提示输入描述和作者姓名。

根据你的喜好填写即可。

图片 4:终端输出,显示 Plasmo 正在为名为 tab-grouper 的新项目搭建结构并安装依赖项。
图片 4:终端输出,显示 Plasmo 正在为名为 tab-grouper 的新项目搭建结构并安装依赖项。

第 3 步:进入你的项目目录

code
cd tab-grouper

第 4 步:查看生成的文件

列出 Plasmo 生成的文件:

code
ls -la

你应该会看到类似如下内容:

code
tab-grouper/
├── .git/                 # Git 仓库(已初始化!)
├── .github/              # GitHub Actions 工作流
├── assets/
│   └── icon.png          # 默认的 Plasmo 图标 
├── node_modules/         # 依赖项(已安装!)
├── package.json          # 项目配置
├── popup.tsx             # 默认弹出页面 
├── .prettierrc.cjs       # 代码格式化规则
├── .gitignore            # Git 忽略规则
├── README.md             # 默认的说明文档
└── tsconfig.json         # TypeScript 配置

需要了解的关键文件:

  • assets/icon.png:Chrome 所需的扩展图标。
  • package.json:列出依赖项和脚本,并用于配置扩展清单(manifest)。
  • popup.tsx:点击扩展图标时显示的用户界面。
  • tsconfig.json:包含已正确配置的 TypeScript 设置。

第 5 步:测试默认扩展

在你进行自定义之前,请确保一切正常工作。

你可以通过启动开发服务器来实现:

code
pnpm dev

你应该会看到类似以下的输出:

code
🟣 Plasmo v0.90.5
🔴 浏览器扩展框架
🔵 INFO   | 启动扩展开发服务器...
🔵 INFO   | 正在构建目标:chrome-mv3
🔵 INFO   | 从 [] 加载环境变量
🟢 DONE   | 扩展已在 1842ms 内重新打包!🚀

查看扩展:
📦 build/chrome-mv3-dev

你的扩展已准备就绪。请保持此终端窗口打开。

Plasmo 会监听文件更改并自动重建。

第 6 步:在 Chrome 中加载扩展

现在将扩展加载到 Chrome 中进行测试:

  1. 打开 Google Chrome
  1. 访问 chrome://extensions/
  1. 启用 开发者模式(右上角的开关)
  1. 点击 “加载已解压的扩展程序”
  1. 导航到你的项目文件夹
  1. 选择 build/chrome-mv3-dev 文件夹
  1. 点击“选择文件夹”
图片 5:动画 GIF,展示如何通过 Chrome 扩展页面的开发者模式加载未打包的扩展程序
图片 5:动画 GIF,展示如何通过 Chrome 扩展页面的开发者模式加载未打包的扩展程序

你的扩展现在应该出现在列表中。

  1. 点击 Chrome 工具栏中的拼图块图标
  1. 找到 "tab-grouper" 并固定它
  1. 点击扩展图标

你会看到一个默认弹窗,上面写着“Welcome to Plasmo!”(欢迎使用 Plasmo!)

图片 6:默认的 Plasmo 弹窗,在 Chrome 工具栏弹窗中显示“欢迎使用 Plasmo”的消息
图片 6:默认的 Plasmo 弹窗,在 Chrome 工具栏弹窗中显示“欢迎使用 Plasmo”的消息

扩展已正常运行。现在你可以开始自定义了。

第 8 步:更新扩展信息

在编辑器中打开 package.json。该文件存储有关项目的元数据,包括名称、版本、描述、依赖项以及用于构建和运行扩展的脚本。

找到顶部附近的这几行:

code
{
  "name": "tab-grouper",
  "displayName": "tab-grouper",
  "version": "0.0.0",
  "description": "A basic Plasmo extension.",

将其修改为:

code
{
  "name": "tab-grouper",
  "displayName": "Tab Grouper",
  "version": "1.0.0",
  "description": "A simple Chrome extension - group tabs by domain",

保存文件。

第 9 步:添加所需权限(关键步骤!)

这是关键一步。 如果没有权限,你的扩展将会报错,例如:

code
TypeError: Cannot read properties of undefined (reading 'query')

Chrome 扩展必须声明它们打算使用的浏览器 API。在 package.json 中找到 "manifest" 部分。

它看起来像这样:

code
"manifest": {
  "host_permissions": [
    "https://*/*"
  ]
}

将其替换为:

code
"manifest": {
  "permissions": [
    "tabs",
    "tabGroups"
  ]
}

保存文件。tabs 权限允许你读取标签页信息(调用 chrome.tabs.query() 所需),而 tabGroups 权限允许你创建和管理标签组(调用 chrome.tabGroups.update() 所需)。

为自己的扩展查找正确的权限:

Chrome 扩展权限参考文档 列出了所有可用权限及其功能。

每个 API 的文档页面也会列出所需的权限,例如 chrome.tabs API 页面 指明需要 "tabs" 权限。

如果你使用的是 Plasmo,可以查阅 清单配置文档,了解如何通过 package.json 添加权限。

一般规则是:当你调用 Chrome API 时遇到 undefined 错误,首先应检查是否缺少权限。

第 10 步:验证热重载是否生效

Plasmo 在你保存更改后会自动重新加载扩展。

查看运行 pnpm dev 的终端。在保存 package.json 后,你应该会看到类似以下内容:

code
🔄 正在重新加载扩展...
✅ 准备就绪,耗时 0.8s

你的项目现已准备就绪:一个在 Chrome 中加载的可运行扩展、一个启用热重载的开发服务器,以及已配置的必要权限。

在接下来的章节中,请保持开发服务器运行并保留已加载的扩展。你的更改将自动重新加载。

本节小结

在本节中,你安装了 pnpm,使用 pnpm create plasmo 脚手架创建了一个新扩展,探索了生成的项目结构,启动了开发服务器,将扩展加载到 Chrome 浏览器中,并更新了扩展的元数据和权限。

下一步: 你将创建用于处理标签页分组逻辑的后台脚本。

理解后台脚本

后台脚本是你的扩展的核心。它在后台持续运行,并包含核心逻辑。

在本例中,该代码负责按域名对标签页进行分组。

什么是后台脚本?

后台脚本会持续运行,即使弹出窗口已关闭也不会停止。

它可以监听浏览器事件(如标签页的打开、关闭或更新),执行无需用户直接交互的任务,并通过消息传递与其他扩展组件通信。

你可以将其视为扩展的“服务端”。而弹出窗口只是与之通信的用户界面。

步骤 1:创建 background.ts

Plasmo 的脚手架默认不会创建后台脚本,因此你需要从零开始创建此文件。在项目根目录下(与 popup.tsx 同级)创建一个名为 background.ts 的新文件:

code
export {}

// 后台脚本 - 在后台运行并处理标签页分组逻辑

console.log("Tab Grouper 后台脚本已加载!")

// 监听来自弹出窗口的消息
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
  if (message.type === "GROUP_TABS") {
    groupTabsByDomain()
    sendResponse({ success: true })
  }
  return true
})

顶部的 export {} 是 Plasmo 所要求的,用于将此文件识别为模块。如果没有它,可能会因全局变量声明冲突而报错。

console.log 帮助你确认脚本是否正确加载(你将在扩展的 DevTools 控制台中看到它)。chrome.runtime.onMessage 设置了一个监听器,使后台脚本能够接收来自弹出窗口的指令。

当接收到 "GROUP_TABS" 消息时,它将调用分组函数。

你可以在 Chrome 扩展文档 中了解更多关于这种消息传递模式的信息。

步骤 2:实现标签页分组逻辑

现在,在消息监听器下方添加主要的分组函数:

code
async function groupTabsByDomain() {
  try {
    // 步骤 1:获取当前窗口中的所有标签页
    const tabs = await chrome.tabs.query({ currentWindow: true })

    // 步骤 2:创建一个 Map,按域名组织标签页
    const domainGroups = new Map<string, chrome.tabs.Tab[]>()

    // 步骤 3:遍历每个标签页并按域名分组
    tabs.forEach(tab => {
      // 跳过没有 URL 的标签页
      if (!tab.url) return

      // 从 URL 中提取域名
      const domain = getDomainFromUrl(tab.url)

      // 跳过无效域名(例如 chrome:// 页面)
      if (!domain) return

      // 将标签页添加到对应的域名分组中
      if (!domainGroups.has(domain)) {
        domainGroups.set(domain, [])
      }
      domainGroups.get(domain)!.push(tab)
    })

    // 步骤 4:为每个域名创建标签页组(仅当有 2 个以上标签页时)
    for (const [domain, domainTabs] of domainGroups) {
      // 跳过只有一个标签页的域名
      if (domainTabs.length < 2) continue

      // 获取所有标签页 ID
      const tabIds = domainTabs
        .map(t => t.id!)
        .filter(id => id !== undefined)

      if (tabIds.length === 0) continue

      // 创建标签页组
      const groupId = await chrome.tabs.group({ tabIds })

      // 使用标题和颜色自定义分组
      await chrome.tabGroups.update(groupId, {

        color: getColorForDomain(domain) // 随机化的标签页组颜色
      })
    }

    console.log(`成功分组 ${domainGroups.size} 个域名`)
  } catch (error) {
    console.error("分组标签页时出错:", error)
  }
}

该函数首先查询当前窗口中的所有标签页,然后遍历这些标签页,构建一个以域名作为键的 Map

当每个标签页都被归入相应的域名分组后,函数遍历该 Map,并对拥有两个或更多标签页的域名调用 chrome.tabs.group(),然后立即使用标题和颜色自定义生成的标签组。

对于仅有一个标签页的域名则跳过,因为单个标签页无需分组。

步骤 3:提取域名辅助函数

添加一个辅助函数,用于从 URL 中提取主机名:

code
function getDomainFromUrl(url: string): string | null {
  try {
    const urlObj = new URL(url)

    // 跳过 Chrome 内部页面(chrome://, chrome-extension://)
    if (urlObj.protocol === "chrome:" || urlObj.protocol === "chrome-extension:") {
      return null
    }

    // 移除 "www." 前缀并返回主机名
    return urlObj.hostname.replace(/^www\./, "")
  } catch {
    // 若 URL 无效则返回 null
    return null
  }
}

new URL(url) 提供了一个结构化对象,避免手动解析 URL 字符串。

协议检查过滤掉了扩展无法访问的 Chrome 内部页面,如 chrome://extensionschrome://settings

.replace(/^www\./, "") 确保 www.github.comgithub.com 被视为同一域名,而不是两个独立的分组。

整个函数包裹在 try-catch 中,以便错误格式的 URL 直接返回 null 并被跳过。

实际效果:https://www.github.com/user/repo 变为 github.comhttps://youtube.com/watch?v=123 变为 youtube.com,而 chrome://extensions 返回 null

步骤 4:颜色分配辅助函数

添加一个函数,用于为每个域名确定性地分配一种颜色:

code
function getColorForDomain(domain: string): chrome.tabGroups.ColorEnum {
  // Chrome 中可用的颜色
  const colors: chrome.tabGroups.ColorEnum[] = [
    "blue", "red", "yellow", "green", "pink", "purple", "cyan", "orange"
  ]

// 基于域名创建一个简单的哈希值 let hash = 0 for (let i = 0; i < domain.length; i++) { hash = domain.charCodeAt(i) + ((hash << 5) - hash) }

// 根据哈希值返回一种颜色 return colors[Math.abs(hash) % colors.length] }

code

Chrome 支持八种标签页组颜色。该函数不是随机分配颜色(那样每次分组时都会改变),而是将域名哈希为一个数字,并使用取模运算符从颜色数组中选择一个一致的索引。

结果是,`github.com` 在不同会话中始终获得相同的颜色,而不同的域名则可能获得不同的颜色。

### 完整的 background.ts 文件

你的完整 `background.ts` 文件应如下所示:

export {}

console.log("Tab Grouper 背景脚本已加载!")

chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { if (message.type === "GROUP_TABS") { groupTabsByDomain() sendResponse({ success: true }) } return true })

async function groupTabsByDomain() { try { const tabs = await chrome.tabs.query({ currentWindow: true }) const domainGroups = new Map<string, chrome.tabs.Tab[]>()

tabs.forEach(tab => { if (!tab.url) return const domain = getDomainFromUrl(tab.url) if (!domain) return

if (!domainGroups.has(domain)) { domainGroups.set(domain, []) } domainGroups.get(domain)!.push(tab) })

for (const [domain, domainTabs] of domainGroups) { if (domainTabs.length < 2) continue

const tabIds = domainTabs .map(t => t.id!) .filter(id => id !== undefined)

if (tabIds.length === 0) continue

const groupId = await chrome.tabs.group({ tabIds })

await chrome.tabGroups.update(groupId, {

color: getColorForDomain(domain) }) }

console.log(成功对 ${domainGroups.size} 个域名进行了分组) } catch (error) { console.error("标签页分组时出错:", error) } }

function getDomainFromUrl(url: string): string | null { try { const urlObj = new URL(url) if (urlObj.protocol === "chrome:" || urlObj.protocol === "chrome-extension:") { return null } return urlObj.hostname.replace(/^www\./, "") } catch { return null } }

function getColorForDomain(domain: string): chrome.tabGroups.ColorEnum { const colors: chrome.tabGroups.ColorEnum[] = [ "blue", "red", "yellow", "green", "pink", "purple", "cyan", "orange" ]

let hash = 0 for (let i = 0; i < domain.length; i++) { hash = domain.charCodeAt(i) + ((hash << 5) - hash) }

return colors[Math.abs(hash) % colors.length] }

code

### 测试背景脚本

如果你的开发服务器尚未从前一节启动,请运行以下命令启动它:

pnpm dev

code

要验证背景脚本是否正确加载,请前往 `chrome://extensions`,找到“Tab Grouper Tutorial”,然后点击 **"service worker"** 链接。

DevTools 控制台将会打开,你应该能看到“Tab Grouper 背景脚本已加载!”的消息,确认一切已正确连接。

弹出窗口(popup)是当用户点击 Chrome 工具栏中的扩展图标时出现的小型窗口。

它可以显示信息、提供操作按钮以及展示设置选项。

在本节中,你将构建一个基于 React 的弹出窗口,用于显示实时的标签页统计信息,并触发背景脚本中的分组逻辑。

当你运行 `pnpm create plasmo` 时,系统会创建一个默认的 `popup.tsx` 文件,仅显示一条欢迎消息。

打开该文件,并用以下起始骨架替换其全部内容:

import { useState, useEffect } from "react"

function IndexPopup() { const [tabCount, setTabCount] = useState(0) const [groupCount, setGroupCount] = useState(0) const [isGrouping, setIsGrouping] = useState(false)

return ( <div> <h2>Tab Grouper</h2> <button>分组标签页</button> </div> ) }

export default IndexPopup

code

保存文件后,扩展程序将自动重新加载。

这三个状态变量分别用于跟踪打开的标签页数量、现有分组数量,以及当前是否正在进行分组操作。

最后一个状态允许我们禁用按钮并显示加载状态,防止用户同时触发多次分组操作。

### 第二步:加载统计数据

现在添加逻辑,在弹出窗口打开时加载标签页和分组的计数。将以下代码添加到 `IndexPopup` 函数内部,紧随状态声明之后:

// 弹出窗口打开时加载标签页统计数据 useEffect(() => { loadStats() }, [])

async function loadStats() { const tabs = await chrome.tabs.query({ currentWindow: true }) const groups = await chrome.tabGroups.query({ windowId: chrome.windows.WINDOW_ID_CURRENT })

setTabCount(tabs.length) setGroupCount(groups.length) }

code

带有空依赖数组 `[]` 的 `useEffect` 会在组件首次挂载时执行一次。也就是说,每次弹出窗口打开时都会运行。

它调用 `loadStats`,向 Chrome 查询当前窗口的标签页和分组,然后使用计数更新状态变量。

### 第三步:触发标签页分组

添加处理函数,在用户点击按钮时向背景脚本发送消息:

async function handleGroupTabs() { setIsGrouping(true)

// 向背景脚本发送消息 await chrome.runtime.sendMessage({ type: "GROUP_TABS" })

// 刷新统计数据 await loadStats() setIsGrouping(false) }

code

`chrome.runtime.sendMessage` 将 `{ type: "GROUP_TABS" }` 消息发送给我们在 `background.ts` 中设置的监听器。

背景脚本完成后,我们刷新统计数据以立即更新分组计数,然后重新启用按钮。

### 第四步:构建 UI

将占位的 `return` 语句替换为以下完整且带样式的版本:

return ( <div style={{ width: 300, padding: 20, fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif' }}> {/* 头部 */} <div style={{ marginBottom: 20 }}> <h2 style={{ margin: 0, fontSize: 20, fontWeight: 600 }}> 🗂️ 标签分组器 </h2> <p style={{ margin: "8px 0 0", fontSize: 13, color: "#666" }}> 按域名整理你的标签页 </p> </div>

{/* 统计信息 */} <div style={{ display: "flex", gap: 12, marginBottom: 20, padding: 12, background: "#f5f5f5", borderRadius: 8 }}> <div style={{ flex: 1 }}> <div style={{ fontSize: 24, fontWeight: 600, color: "#333" }}> {tabCount} </div> <div style={{ fontSize: 12, color: "#666" }}> 打开的标签页 </div> </div> <div style={{ flex: 1 }}> <div style={{ fontSize: 24, fontWeight: 600, color: "#0066ff" }}> {groupCount} </div> <div style={{ fontSize: 12, color: "#666" }}> 标签组 </div> </div> </div>

{/* 分组按钮 */} <button onClick={handleGroupTabs} disabled={isGrouping} style={{ width: "100%", padding: "12px 16px", fontSize: 14, fontWeight: 500, color: "white", background: isGrouping ? "#ccc" : "#0066ff", border: "none", borderRadius: 8, cursor: isGrouping ? "not-allowed" : "pointer", transition: "background 0.2s" }} > {isGrouping ? "正在分组..." : "🗂️ 按域名分组标签页"} </button>

{/* 底部提示 */} <div style={{ marginTop: 16, padding: 12, fontSize: 12, color: "#666", background: "#fff9e6", borderRadius: 6, border: "1px solid #ffe066" }}> 💡 <strong>提示:</strong> 此操作将按网站域名对当前窗口中的所有标签页进行分组。 </div> </div> )

code

该界面包含四个部分:带有扩展名称和简短描述的头部、并排显示实时标签页数量和分组数量的统计框、主操作按钮(工作进行时会变灰并显示“正在分组...”),以及底部的提示框。

本教程为简化起见使用了内联样式。在生产环境的扩展中,你可能会选择使用 CSS 模块、Tailwind 或 styled-components。

你的完整 `popup.tsx` 文件应如下所示:

import { useState, useEffect } from "react"

function IndexPopup() { const [tabCount, setTabCount] = useState(0) const [groupCount, setGroupCount] = useState(0) const [isGrouping, setIsGrouping] = useState(false)

useEffect(() => { loadStats() }, [])

async function loadStats() { const tabs = await chrome.tabs.query({ currentWindow: true }) const groups = await chrome.tabGroups.query({ windowId: chrome.windows.WINDOW_ID_CURRENT })

setTabCount(tabs.length) setGroupCount(groups.length) }

async function handleGroupTabs() { setIsGrouping(true) await chrome.runtime.sendMessage({ type: "GROUP_TABS" }) await loadStats() setIsGrouping(false) }

return ( <div style={{ width: 300, padding: 20, fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif' }}> <div style={{ marginBottom: 20 }}> <h2 style={{ margin: 0, fontSize: 20, fontWeight: 600 }}> 🗂️ 标签分组器 </h2> <p style={{ margin: "8px 0 0", fontSize: 13, color: "#666" }}> 按域名整理你的标签页 </p> </div>

<div style={{ display: "flex", gap: 12, marginBottom: 20, padding: 12, background: "#f5f5f5", borderRadius: 8 }}> <div style={{ flex: 1 }}> <div style={{ fontSize: 24, fontWeight: 600, color: "#333" }}> {tabCount} </div> <div style={{ fontSize: 12, color: "#666" }}> 打开的标签页 </div> </div> <div style={{ flex: 1 }}> <div style={{ fontSize: 24, fontWeight: 600, color: "#0066ff" }}> {groupCount} </div> <div style={{ fontSize: 12, color: "#666" }}> 标签组 </div> </div> </div>

<button onClick={handleGroupTabs} disabled={isGrouping} style={{ width: "100%", padding: "12px 16px", fontSize: 14, fontWeight: 500, color: "white", background: isGrouping ? "#ccc" : "#0066ff", border: "none", borderRadius: 8, cursor: isGrouping ? "not-allowed" : "pointer", transition: "background 0.2s" }} > {isGrouping ? "正在分组..." : "🗂️ 按域名分组标签页"} </button>

<div style={{ marginTop: 16, padding: 12, fontSize: 12, color: "#666", background: "#fff9e6", borderRadius: 6, border: "1px solid #ffe066" }}> 💡 <strong>提示:</strong> 此操作将按网站域名对当前窗口中的所有标签页进行分组。 </div> </div> ) }

export default IndexPopup

code

## 测试你的扩展

现在你已经构建好了后台脚本和弹出界面,是时候在 Chrome 中验证一切是否正常工作了。

### 第一步:确保开发服务器正在运行

如果之前的步骤中没有运行 `pnpm dev`,现在启动它:

pnpm run dev # 或 pnpm dev

code

Plasmo 将把扩展构建到 `build/chrome-mv3-dev` 目录并监听文件变化。

### 第二步:在 Chrome 中加载扩展

如果你还没有加载扩展,请前往 `chrome://extensions/`,启用 **开发者模式**,点击 **加载已解压的扩展程序**,然后选择 `build/chrome-mv3-dev` 文件夹。

加载后,你应该能在列表中看到名为“Tab Grouper Tutorial”、版本为“1.0.0”且状态为“已启用”的扩展。

### 第三步:固定扩展

点击 Chrome 工具栏中的拼图图标,找到“Tab Grouper Tutorial”,然后点击图钉图标以保持其常驻显示。

现在,该扩展程序的图标将直接出现在你的工具栏中。

### 第 4 步:测试扩展程序

#### 测试 1:打开多个标签页

在几个不同的域名下打开多个标签页,以便进行分组测试:

1.   `https://github.com/topics`, `https://github.com/trending`, `https://github.com/explore`

2.   `https://www.youtube.com/` 和 `https://www.youtube.com/trending`

3.   `https://stackoverflow.com/questions` 和 `https://stackoverflow.com/tags`

请确保至少打开 7 个标签页。

#### 测试 2:对标签页进行分组

点击 Tab Grouper 扩展图标。弹出窗口应显示你当前打开的标签页数量(7 个或更多)以及分组数量(可能为 0)。

点击 **"按域名分组标签页"**,观察你的标签页被自动组织成不同颜色的组。

#### 测试 3:验证分组结果

点击按钮后,GitHub 的标签页应被归入一个带有 "github.com" 标签和统一颜色的组,YouTube 的标签页也应类似处理。

再次点击扩展图标,此时分组数量应显示为 2,而标签页总数保持不变。

### 第 5 步:调试扩展程序

如果某些功能未正常工作,Chrome 的 DevTools 是你最好的帮手。

要检查后台脚本,请前往 `chrome://extensions/`,找到你的扩展程序,点击 **"service worker"** 链接。

DevTools 控制台将打开,你可以查找 “Tab Grouper background script loaded!” 消息,以及任何红色显示的错误信息。

要检查弹出窗口,右键点击扩展图标并选择 **"Inspect popup"**。这会为弹出窗口单独打开 DevTools —— 请查看 Console 标签页中的错误。

**如果你点击按钮后没有任何反应**,请检查后台脚本控制台中的错误,确认你至少有两个来自同一域名的标签页,并验证消息是否已正确发送(在弹出窗口控制台中查看是否有 `sendMessage` 失败的情况)。

**如果标签页没有被分组**,请仔细检查是否已在 `package.json` 中添加了 `tabs` 和 `tabGroups` 权限,并在保存后重新加载了扩展程序。

**如果你看到“Extension cannot access chrome://...”的提示**,这是预期行为 —— 扩展程序无法与 Chrome 内部页面交互,代码会主动跳过这些页面。

### 第 6 步:热重载

Plasmo 的一大优势是支持热重载(hot reloading),允许你在应用运行时即时更新代码,无需手动重启。

打开 `popup.tsx`,将标题的 emoji 从 🗂️ 改为 📁,然后保存。

扩展程序将自动重新加载。

点击图标,你会立即看到更新后的 emoji。

热重载的优势在于加快开发速度,让你能实时查看更改效果。

如果你想让扩展程序与本教程其余示例和截图保持一致,之后可以再把 emoji 改回去。

### 第 7 步:测试边界情况

值得测试几种特殊情况,以确保扩展程序能优雅地处理它们。

如果你关闭所有标签页只剩一个,然后点击“分组标签页”,不应有任何反应。扩展程序需要至少两个来自同一域名的标签页才能形成分组。打开 `chrome://extensions` 和 `chrome://settings` 后再尝试分组也应无反应,因为这些页面已被过滤排除。

如果你有一个来自 `reddit.com` 的标签页和一个来自 `freecodecamp.org` 的标签页,且每个域名仅出现一次,则不应创建任何分组。

### 第 8 步:生产构建

当你准备分享你的扩展程序时,运行:

pnpm run build

code

这将在 `build/chrome-mv3-prod` 目录下生成一个针对生产环境优化的版本:JavaScript 被压缩,移除了仅用于开发的代码,文件体积更小。

要验证生产构建,请前往 `chrome://extensions/`,移除开发版本,点击“加载已解压的扩展程序”,然后选择 `build/chrome-mv3-prod`。发布前请充分测试。

该扩展程序非常轻量(小于 100 KB),仅在你点击按钮时运行,空闲时无后台进程。

## 后续步骤与扩展创意

恭喜你完成了你的第一个 Chrome 扩展!

你现在拥有一个可正常工作的工具:一键即可按域名对标签页进行分组,实时显示打开的标签页和分组数量统计,并基于现代技术栈构建:TypeScript、React 和 Plasmo,遵循 Chrome 扩展的最佳实践。

这个扩展打下了坚实的基础。以下是一些后续改进方向的建议。

### 1. 自动分组

你可以改为在新标签页打开时自动进行分组,而无需手动点击按钮。可以在 `background.ts` 中监听 `chrome.tabs.onCreated` 事件,并稍作延迟后调用 `groupTabsByDomain()`,以便等待页面 URL 加载完成:

// 在 background.ts 中 chrome.tabs.onCreated.addListener(async (tab) => { // 等待一段时间让 URL 加载 setTimeout(() => { groupTabsByDomain() }, 2000) })

code

这涉及事件监听器、异步时序控制,以及何时触发的思考 —— 是理解如何让后台脚本更具主动性的良好进阶练习。

### 2. 键盘快捷键

你可以通过添加键盘快捷键来触发分组,甚至无需打开弹出窗口。在 `package.json` 的 manifest 中添加 `commands` 字段:

"manifest": { "commands": { "group-tabs": { "suggested_key": { "default": "Ctrl+Shift+G", "mac": "Command+Shift+G" }, "description": "按域名分组标签页" } } }

code

然后在 `background.ts` 中监听该命令:

chrome.commands.onCommand.addListener((command) => { if (command === "group-tabs") { groupTabsByDomain() } })

code

### 3. 按类别分组

你可以不按原始域名分组,而是按类别分组 —— 例如将 GitHub、Stack Overflow 和 npm 归入一个“开发”组:

const categories = { social: ["facebook.com", "twitter.com", "instagram.com"], shopping: ["amazon.com", "ebay.com", "etsy.com"], dev: ["github.com", "stackoverflow.com", "npmjs.com"] }

code

function getCategoryForDomain(domain: string): string { for (const [category, domains] of Object.entries(categories)) { if (domains.includes(domain)) { return category } } return "other" }

code

### 4. 选项页面

Plasmo 只需创建一个 `options.tsx` 文件,就能轻松添加设置页面。

在这里,你可以让用户开启或关闭自动分组功能、在“按域名”和“按类别”模式之间切换,或者自定义自己的类别映射。

这也是了解 Chrome Storage API 和持久化保存用户偏好设置的良好入门实践。

function OptionsPage() { return ( <div> <h1>标签页分组设置</h1> <label> <input type="checkbox" /> 启用自动分组 </label> <label> <input type="checkbox" /> 按类别而非域名进行分组 </label> </div> ) }

code

### 5. 标签页使用时长追踪

你可以记录每个标签页的创建时间,并找出那些已闲置一周甚至更久的标签页,以此鼓励用户保持良好的标签页管理习惯:

// 记录标签页创建时间 const tabCreationTimes = new Map<number, number>()

chrome.tabs.onCreated.addListener((tab) => { if (tab.id) { tabCreationTimes.set(tab.id, Date.now()) } })

// 查找旧标签页(例如超过7天) function getOldTabs(): chrome.tabs.Tab[] { const sevenDaysAgo = Date.now() - (7 * 24 * 60 * 60 * 1000) return tabs.filter(tab => { const created = tabCreationTimes.get(tab.id!) return created && created < sevenDaysAgo }) }

code

### 6. 组内搜索

在弹出窗口中添加一个搜索栏,允许用户通过标题过滤打开的标签页,从而快速跳转到特定标签页:

const [searchQuery, setSearchQuery] = useState("")

const filteredTabs = tabs.filter(tab => tab.title?.toLowerCase().includes(searchQuery.toLowerCase()) )

code

### 7. 导出/导入分组

你可以允许用户将当前的标签页分组保存为 JSON 文件,并在未来恢复它们。这对于跨浏览器重启保留工作会话非常有用:

// 导出 async function exportGroups() { const groups = await chrome.tabGroups.query({}) const data = JSON.stringify(groups) const blob = new Blob([data], { type: 'application/json' }) const url = URL.createObjectURL(blob) chrome.downloads.download({ url, filename: 'tab-groups.json' }) }

// 导入 async function importGroups(file: File) { const text = await file.text() const groups = JSON.parse(text) // 恢复分组... }

code

### 8. 分组统计仪表板

扩展后的弹出窗口可以展示浏览统计数据,如今天总共打开了多少标签页、访问最频繁的域名等:

function Statistics() { const [stats, setStats] = useState({ totalTabs: 0, totalGroups: 0, mostUsedDomain: "", tabsToday: 0 })

return ( <div> <h3>浏览统计</h3> <p>今日总共打开标签页数:{stats.tabsToday}</p> <p>访问最多的域名:{stats.mostUsedDomain}</p> </div> ) }

code

## 学习资源

如果你想深入学习,[官方 Chrome 扩展文档](https://developer.chrome.com/docs/extensions/) 非常出色,详细介绍了每一个 API。

GitHub 上的 [Chrome 扩展示例仓库](https://github.com/GoogleChrome/chrome-extensions-samples) 提供了数十个真实可用的示例供你学习参考。对于 Plasmo 特有的问题,[Plasmo 官方文档](https://docs.plasmo.com/) 和 [示例仓库](https://github.com/PlasmoHQ/examples) 是最佳起点,同时社区成员活跃于 [Plasmo Discord](https://www.plasmo.com/community) 中。

[React 官方文档](https://react.dev/) 和 [TypeScript 官方文档](https://www.typescriptlang.org/docs/) 值得收藏作为参考资料,而当你对某些类型写法不确定时,[React TypeScript Cheatsheet](https://react-typescript-cheatsheet.netlify.app/) 是一个非常实用的工具。

如需社区支持,Stack Overflow 上的 `chrome-extension` 标签受到良好维护,Reddit 上的 r/chrome_extensions 社区也是一个提问交流的友好场所。

## 发布到 Chrome 网上应用店

现在你已经构建并测试好了你的扩展程序,接下来介绍如何发布它并与全世界分享。

### 所需准备

在发布之前,你需要准备好一个已完成并经过测试的扩展程序、一个 Google 账号、一次性支付 5 美元的开发者注册费用,以及一些商店所需的资源文件,例如图标、截图和文字描述。

这 5 美元是一次性费用(非年度收费),Google 用它来验证开发者身份并减少垃圾信息。该费用涵盖无限次提交扩展程序,通过 Google Payments 立即完成扣款。

### 第一步:生成生产版本构建

如果你之前没有做过,请为生产环境构建你的扩展程序:

cd tab-grouper-tutorial npm run build

code

这将在 `build/chrome-mv3-prod/` 目录下生成一个优化后的版本。生产构建会对 JavaScript 和 CSS 进行压缩以减小文件大小,移除仅用于开发的代码和控制台日志,并优化资源以实现更快加载。

上传前,请先将 `build/chrome-mv3-prod/` 作为未打包扩展程序加载进来,再次全面测试所有功能,确保构建过程中没有引入任何问题。

### 第二步:创建商店资源

#### 扩展图标

你需要提供三种尺寸的图标:**128×128 像素**(用于主商店列表,必填)、**48×48**(用于扩展管理页面)和 **16×16**(用作网站 favicon)。

所有图标都应为带透明背景的 PNG 文件。设计应简洁明了,在小尺寸下仍清晰可辨。避免在 16×16 图标中加入文字。

[Figma](https://figma.com/) 免费且非常适合此用途,[Canva](https://canva.com/) 或 [GIMP](https://gimp.org/) 也是不错的选择。

#### 截图

上传 1 到 5 张截图,尺寸为 1280×800 或 640×400 像素(PNG 或 JPEG 格式均可)。

尽量展示扩展的实际使用场景,而不是静态设计稿。例如弹出窗口中的统计数据、标签页正在被分组的过程、以及分组前后的对比状态都非常合适。

添加标注以突出关键功能,有助于用户理解他们看到的内容。

#### 宣传图片(可选)

如果你想在商店中获得推荐展示,还可以上传一个小磁贴(440×280)、大磁贴(920×680)和横幅图片(1400×560)。这些仅在 Google 决定推广你的扩展程序时才需要。

#### 演示视频(可选)

一段简短的 YouTube 视频(30–60 秒),展示扩展程序的实际使用效果,可以显著提高转化率。在你的商店列表中添加该视频链接。

第 3 步:撰写商店列表内容

扩展名称(45 字符限制):清晰且具有描述性。“Tab Grouper - 按域名整理标签页”就是一个很好的例子。避免关键词堆砌或过度使用标点符号。

摘要(132 字符限制):这是在搜索结果中显示的内容。开头应说明扩展的功能:“自动按域名整理浏览器标签页。一键分组,保持工作区整洁高效。”

详细描述(16,000 字符限制):从扩展功能开始,清晰列出特性,说明使用方法,阐述隐私保护措施,并提供联系方式。以下是一个可自定义的模板:

code
## 什么是 Tab Grouper?

Tab Grouper 会根据网站域名自动整理你的浏览器标签页,将它们分组管理。再也不用在几十个标签页中翻找——一切井然有序。

## 功能特点

- ✅ 一键标签分组
- ✅ 按域名自动颜色编码
- ✅ 实时统计信息
- ✅ 兼容所有网站
- ✅ 轻量快速

## 如何使用

1. 点击工具栏中的 Tab Grouper 图标
2. 点击“按域名分组标签”
3. 标签页立即完成整理

## 为什么你需要它

如果你经常打开大量标签页,寻找正确的那一个会浪费宝贵时间。Tab Grouper 通过将标签页自动归入彩色分组,让导航变得快速而简单。

## 隐私保护

本扩展不收集任何个人数据。仅在本地访问标签信息以执行分组操作,不会向外部服务器发送任何数据。

## 支持

发现 Bug 或有建议?请联系我们:[email protected]

类别:为 Tab Grouper 选择 效率工具。如果需要本地化列表,之后可添加其他语言。

第 4 步:注册成为 Chrome 网上应用店开发者

前往 Chrome 网上应用店开发者控制台,使用你的 Google 账户登录,接受开发者协议,并支付 5 美元注册费。你的账户将在几分钟内激活。

第 5 步:提交你的扩展程序

在开发者控制台中,点击 “新建项目” 并上传你的扩展程序。你可以手动压缩 build/chrome-mv3-prod/ 文件夹,也可以使用 Plasmo 的打包命令:

code
# 选项 1:手动压缩
cd build/chrome-mv3-prod
zip -r ../../tab-grouper.zip .

# 选项 2:使用 Plasmo 打包命令
cd tab-grouper-tutorial
npm run package

上传后,填写商店列表表单的全部四个部分:产品详情(名称、摘要、描述、类别、语言)、图形资源(图标和截图)、隐私实践(见下文)以及 分发设置(可见性、地区、定价)。

#### 单一用途说明

Chrome 要求每个扩展必须有一个明确声明的单一用途。对于 Tab Grouper:“此扩展通过基于域名对浏览器标签页进行分组来实现整理,帮助用户高效管理多个打开的标签页。”

#### 权限说明

你需要为所声明的每一项权限提供合理解释。对于 tabs:“需要 tabs 权限以读取标签页的 URL 和标题,从而按域名进行分组。” 对于 tabGroups:“需要 tabGroups 权限以创建和管理标签组,实现组织功能。”

#### 隐私政策

尽管 Tab Grouper 不收集个人数据,Chrome 可能仍要求提供隐私政策。你可以将其托管在 GitHub Pages 或个人网站上并提供链接。以下是一个极简模板:

code
# Tab Grouper 隐私政策

## 数据收集
Tab Grouper 不收集、存储或传输任何个人数据。

## 权限说明
- **tabs**:仅用于读取标签页 URL 以实现分组
- **tabGroups**:仅用于创建和管理标签组

## 本地处理
所有标签分组操作均在你的浏览器本地完成。无任何数据发送至外部服务器。

## 联系方式
如有疑问,请联系:[email protected]

最后更新日期:[当前日期]

第 6 步:提交审核

在点击提交前,请检查以下清单:

  • 已充分测试生产版本
  • 已上传所有商店资源(图标 + 至少一张截图)
  • 描述清晰准确
  • 所有权限均已说明
  • 已链接隐私政策
  • 扩展名称具有描述性

准备就绪后,点击 “提交审核”,确认信息无误,然后点击 “发布”。你的扩展将进入审核队列。

第 7 步:审核流程

对于简单的提交,Google 通常在 1–3 个工作日内完成审核;复杂扩展或首次提交可能需要长达一周时间。审核人员会检查扩展是否如描述般运行,权限是否合理,是否存在恶意代码,以及列表是否符合 Chrome 网上应用店政策。

你可以在开发者控制台跟踪状态:待审核 → 审核中 → 已批准或已拒绝。如果被拒,Google 会通过邮件告知具体原因及重新提交的指导。

最常见的拒绝原因包括:权限说明不足、描述误导、缺少隐私政策、请求了超出必要的权限。请根据拒绝邮件中的每一点进行修改,更新提交内容后重新提交。

第 8 步:审核通过后

一旦获批,你的扩展将在 https://chrome.google.com/webstore/detail/[extension-id] 上线。你可以将链接分享到社交媒体,撰写博客文章,在 Reddit(r/chrome, r/chrome_extensions)发帖,或提交至 Product Hunt,以吸引更多安装。

开发者仪表板会为你提供持续的分析数据——总安装量和每周安装量、评价与评分、展示次数以及卸载数量。请定期查看,尤其是在发布的第一周内。及时回复用户评论(特别是负面评论),感谢用户的积极反馈,并利用报告中的 bug 来优先安排后续更新。

第 9 步:发布更新

当你修复了 bug 或添加了新功能时,请在 package.json 中增加版本号(遵循 语义化版本控制 规范——bug 修复使用补丁版本 patch,新增功能使用次版本 minor,破坏性变更使用主版本 major),运行 npm run build,然后通过开发者仪表板的 Package 标签页上传新的包。更新通常比初次提交审核更快,通常在 24 小时内完成。

第 10 步:长期维护你的扩展程序

Chrome 网上应用店提供内置的分析功能,但如果你需要更详细的数据,也可以集成 Google Analytics。

对于用户支持,可以在描述中提供一个邮箱地址,或使用 GitHub 的 Issues 页面,两者都很有效。随着功能的增加,请保持描述内容的更新,并维护一份变更日志,让用户清楚知道每次更新的内容和时间。积极回应用户的问题和评论,有助于建立一批忠实用户,他们将乐于向他人推荐你的扩展。

常见发布问题排查

上传时提示“包无效”:请确保你压缩的是 build/chrome-mv3-prod/ 文件夹内的内容,而不是该文件夹本身,并验证生成的 manifest.json 是否为有效的 JSON 格式。

被拒:权限未说明清楚:在“权限说明”字段中,具体说明每一项权限对应的功能,以及缺少该权限会导致什么功能失效。

被拒:单一用途不明确:重写单一用途描述,聚焦于一个核心功能,并用清晰直白的语言表达。

发布后安装率低:问题常出在截图质量差——这是大多数用户首先看到的内容。确保截图能清晰展示扩展如何解决实际问题。在上线初期积累少量正面评价也会显著提升新访客的转化率。

其他分发方式

对于大多数公开扩展程序来说,Chrome 网上应用店是最佳选择。如果你正在开发内部工具,可以选择发布为 非列表型 扩展(仅可通过直接链接访问,无法被搜索到),这是一个不错的选择。

如果你希望将扩展限制为仅特定 Google Workspace 组织内的用户使用,则可以发布为 私有 扩展。你也可以选择自行托管并侧载安装,但这要求用户手动启用开发者模式,因此只适合技术能力较强的用户群体。

恭喜你!

你已经从一个空文件夹,成功打造出一款上线到 Web Store 的 Chrome 扩展程序。在此过程中,你学习了扩展程序的结构、后台脚本与弹窗之间的通信机制、Chrome 的标签页 API 工作原理,以及如何完整走完整个发布流程。

比起任何具体的 API 或配置细节,你最重要的收获是对扩展程序工作机制建立起的心智模型,这种理解将直接适用于你未来想要实现的任何扩展创意。

继续构建,持续学习,不断发布!

  • * *
  • * *

免费学习编程。freeCodeCamp 的开源课程已帮助超过 40,000 人成为开发者。立即开始