Chrome Extension SaaS Boilerplate:7 个模块(实战笔记)

本文详细记录了如何使用 TanStack Ship 在短短 11 天内开发出一套包含七个核心模块的 Chrome 插件与 SaaS 伴侣产品架构。通过这份来自一线生产环境的真实实战笔记,你将深入了解实现 50ms 冷启动延迟和零闲置托管成本的具体构建细节、完整技术栈工作流以及精确到每月的各项成本核算数据。

Huifer
Huifer
May 5, 202612 min read
其他语言:Deutsch · English

本文由 Huifer 撰写,我是 TanStack Ship 的独立开发者兼维护者。在 2026 年 3 月,我用了 11 个工作日为一款创作者工具产品发布了 Chrome 插件 + SaaS 伴侣产品架构。下文中的七大模块布局、$0 闲置成本以及 50ms 冷启动时间均来自该次实际构建,而非营销幻灯片。我在第 11 天将其部署到生产环境,在第 14 天便收获了 47 名付费客户,到第三个月付费用户数达到了 412 人。

验证来源: Chrome MV3 Manifest Reference · Chrome Network Requests Docs · TanStack Start Documentation · Cloudflare Workers Documentation · Cloudflare D1 Documentation · Cloudflare Durable Objects · Better Auth Edge Session Guide · TanStack Ship GitHub

最后更新: 2026-05-05 · Changelog

太长不看 (TL;DR):“Chrome 插件 + SaaS”架构是一个垂直领域,而不是两个独立产品。TanStack Ship 提供了你所需的七个模块(插件清单、SaaS 应用、身份验证桥接、Stripe 计费、边缘同步层、管理控制台、部署流水线),只需运行一次 pnpm dev 即可将它们连接在一起。闲置总成本:$0。冷启动 p99:50ms。真实产品的端到端构建时间:11 天。以下是详细内容:实战笔记、这七大模块、成本核算、生产环境的代码,以及三种不该选择此技术栈的场景。


为什么 Chrome 插件 + SaaS 是一个真正的垂直领域

浏览器插件曾经只是单人周末的小型项目。但到了 2026 年,它们已经成长为一个拥有自身营收模式、获客循环以及单位经济效益的产品类别。我在 2026 年 3 月发布的创作者工具 Chrome 插件,于第三个月达到了 $1,800 的 MRR(月经常性收入)和 412 名付费用户,而 SaaS 伴侣产品(用于使用情况分析和团队管理的控制台)才是主要的收入来源,而非插件本身。

市场在方方面面都已经成熟。从 2024 年起,MV3 已成为 Chrome Web Store 的唯一标准。Stripe 提供了专用的插件计费模式,包含了插件内的支付结账流程。而 Cloudflare Workers——详见 Workers 文档——让小规模的后端变得极其廉价,因为它们将运行时折叠到 V8 隔离区中,可以在几毫秒内启动,而不是拉取 Node 容器。这个垂直领域是真实的,其单位经济效益也十分可观。

该类别主要有三种产品形态,而这七模块布局仅适用于其中一种。弄清楚你要构建的是哪种形态,将决定后续的所有工作:

  • 浏览器优先 (Browser-first) —— 插件即产品;SaaS 控制台是可选的(例如 1Password,Grammarly)。SaaS 带来的收入微乎其微。大多数插件都无法摆脱这一形态。
  • 伴侣产品架构 (Companion-product stack) —— 插件是日常操作的主界面;SaaS 则是管理团队、计费和数据分析的地方(例如 Loom,Notion Web Clipper,Reader Mode 应用)。这里产生大部分的收入。这也正是本文所探讨的形态。
  • 插件作为引流渠道 (Extension-as-channel) —— SaaS 才是真正的产品;插件仅作为营销和获客的触点(例如 Linear,Superhuman)。这种形态下,插件的用户粘性最低。有着不同的经济模型。

本文讨论的是中间这种形态——伴侣产品架构——因为这正是 TanStack Ship 七模块布局大显身手的地方。另外两种形态需要不同的技术栈,本文底部的决策树会为你指明方向。

协同发布的 7 个模块

以下是我在开发创作者工具产品时遵循的构建顺序。每个模块花费了半天到两天不等的时间。顺序非常重要:首先部署一个可用的 MV3 清单迫使我尽早做出 UX(用户体验)上的选择,这能避免在 SaaS 侧过度设计,并让这七个模块保持纯粹。

模块 1:插件清单 (MV3) + 消息通信层

第一次提交即是一个可运行的 MV3 清单文件,其可以在任意页面加载,注入内容脚本(content script),并向插件的 Service Worker 完成一轮消息收发通信。没有 SaaS,没有身份验证,也没有 UI。Chrome 团队提供的 MV3 manifest reference 和 network requests docs 是我开发此模块所需的仅有的两份文档。

MV3 强制你放弃使用背景页(background pages),转而采用 Service Workers。Service Workers 是事件驱动的,并且在不活跃 30 秒后会自动关闭。这意味着:你想要持久化的任何状态都必须存放在 chrome.storage.session(浏览器重启时会清除)或 chrome.storage.local(跨会话持久保存)中。TanStack Ship 的插件模板通过类型安全的封装对其进行了处理,使代码库的剩余部分可以将它们当作同步的键值存储来使用。

模块 2:SaaS 伴侣产品 (TanStack Start)

这是用来托管团队控制台、计费门户以及管理工具的 Web 应用。我使用了 TanStack Start,因为其 Server Functions 的模式让我能在同一个代码库里编写插件的 API 端点与 SaaS 的路由,并且共享类型。实现了从插件弹窗到 Service Worker、再到 Cloudflare Worker 和 D1 的端到端类型安全。没有 API 客户端代码生成的步骤,无需手动同步数据模型,也不会出现偏差。

SaaS 侧同时也承载了营销界面:落地页、定价、博客和登录。TanStack Ship 预先构建好了这些路由,集成了 Stripe 计费体系,使用 Paraglide JS 处理 i18n,并配置好了 SEO 的 meta 标签。正如我们对 pricing comparison breakdown 中提到的那样,所有围绕费用成本的计算大多发生在这里。

模块 3:身份验证桥接 (在插件与 SaaS 之间共享 Better Auth 会话)

这是最难的部分。由于宿主页面上的第三方 cookie 限制,插件弹出窗口无法可靠地使用 cookie;而 SaaS 使用了 cookie。如果没有一个桥接层,你最终会得到两套平行的登录流程,这会让用户感到困惑。

我通过从 SaaS 登录端点专门签发一个短期的(15 分钟临时)加密 JWT,来实现共享 Better Auth 的会话。插件将该 JWT 存储在 chrome.storage.session 中,并在每次发起 API 调用时作为 Authorization: Bearer 附加在请求头中。超时后进行刷新操作。这座桥接层对功能实现代码是隐形的,因为它独立存放在插件和 SaaS 共享引入的 lib/ 目录下。

会话验证发生在 Worker 侧,使用的是 Better Auth 兼容边缘环境的验证器。用不到那些仅限 Node 环境支持的加密库或 jsonwebtoken 这类折中方案。整个桥接层加上测试也不过约 80 行代码。

模块 4:Stripe 计费服务器 (幂等性 Webhook)

升级使用的 Stripe Checkout,降级访问的客户门户(customer portal),以及基于 event.id 实现幂等性的 webhook 处理器(这个模式是我得到了惨痛教训后学到的——具体的故障情况详见我们文档里的复盘)。这个 webhook 被包裹在一个 Durable Object 中,从而在 Stripe 发生各种网络重试请求时依然能够保证“恰好一次”(exactly-once)的处理机制。

计费模块开箱即用地提供了三种流程:一次性购买、周期性订阅和按量计费(metered usage)。由于这款创作者工具产品采用的是按量订阅(免费用户按获取高亮的使用量计费,付费用户为固定订阅费用)。我只花了半天的时间,便增加了第四种工作流(基于席位的团队定价模式),因为底层 schema 和 webhook 处理器在设计之初就具备良好扩展性。

模块 5:Cloudflare Workers 同步层 (KV + Durable Objects)

主要负责在插件状态和 SaaS 之间提供实时同步。使用 Cloudflare Durable Objects 持有记录用户级别的在线和实时编辑状态;并基于 KV 维护功能开关标志和运行配置项。而 Workers 将两者无缝衔接在一起。

这也是花了我最长时间才完善的设计模式:插件发出的一条“保存高亮内容(save highlight)”事件,需要在不到一秒内完成更新写入 SaaS 管理面板,在此期间无需用户主动去刷新页面。解决方案是为每一位该用户分配一个专属的 Durable Object,这充当了一份存有最近高亮内容的内存列表,并通过异步模式写回记录到 D1 数据库里。SaaS 控制台在每次加载网页时从 D1 获取数据,随后监听订阅 Server-Sent Events 流,从而保持实时更新。此时它的实时端到端延迟中位数为 200ms,在 p99 的情况为 800ms。

模块 6:管理控制台 (插件安装分析、MRR、流失率)

功能囊括:MRR / ARR 仪表盘、群组流失量(churn cohorts)分析、插件安装漏斗,以及“前 30 日激活”视图。该模块堪称“为整个全栈带来业务价值的基础”:如果失去它,你根本辨识不出哪些营销渠道带来了真正付费的用户,而哪些渠道引来的只是免费层的白嫖者。

对于这款创作者工具产品,激活视图在第一周就暴露了一个残酷的真相:高达 62% 的插件安装者从未第二次打开过弹窗。仅仅这一个指标就推动了三项产品改动,将第 30 天的留存率从 18% 提升到了 41%。这是对代码库进行任何程度的静态分析都无法发现的。

模块 7:边缘函数部署 + GitHub Action

这个 GitHub Action 会在主分支 main 上发生代码推送时自动将 SaaS 部署到 Cloudflare Workers,并在打上 tag 标签时将插件发布到 Chrome Web Store。部署流水线是所有人都会低估的一个不够性感的模块——我的流水线配置花费了整整一天的时间,因为涉及 Chrome Web Store 的审核队列(更新平均需要 3 天,首次提交需要 7 天)以及获取发布 API 令牌过程中繁琐的 OAuth 认证舞蹈(OAuth dance)。

该 action 还会运行 Worker 打包体积检查,如果任何 Server Function 超过 1MB(Workers 的限制),则部署会失败。单单这一个检查就在产品到达生产环境之前捕获了三次倒退(regressions)。如果没有它,一次繁重的 Stripe SDK 导入就会悄悄地将你推过打包限制,最终导致 Workers 拒绝部署。

你可以跳过的(以及你仍然需要构建的)

上面的七个模块并不是“免费”的。TanStack Ship 给你提供了这些“连线”支持:项目结构、授权身份黏合层、计费集成对接底座功能、构建部署流水线链路、以及管理后台架构(admin shell)。但你仍然必须要自己亲自去开发完成发布的还有:

  • 实际的产品界面 —— 弹出窗口 UI、内容脚本与宿主页面的交互,以及 SaaS 控制台的独特功能。这才是真正的工作所在,没有任何样板代码(boilerplate)能代替你来完成。
  • 品牌与设计 —— 颜色、排版、插画以及动效。TanStack Ship 提供了一个基于 Tailwind 的极简默认主题,你只需大约一天的时间即可将其替换。
  • 文案 —— 落地页、应用内引导,以及邮件自动化序列。样板代码中内置提供了占位文案,一旦你明确了自己的产品定位,便可以开始对其进行大范围重写。
  • Chrome Web Store 商店资产 —— 截图、促销宣传图、说明描述、隐私条款声明。这些资产都不会随代码一起提供。

在开发这款创作者工具的过程中,我估计有 60% 的时间(总计 11 天)花在了产品界面和品牌设计上,25% 用于架构连线工作(这已被样板代码所减免解决),剩下 15% 则用于打磨细节并处理极端边缘情况(edge cases)。

成本核算:闲置成本 $0,1 万 MAU 仅需 $0.50/月

在拥有 412 名付费用户的情况下,这款创作者工具插件的各项服务托管账单(2026 年 3 月发票):

账单计费项目成本
Cloudflare Workers (每月 500 万次请求)$0.00 (免费计划)
Cloudflare D1 (200MB,每月 800 万次读取,每月 8 万次写入)$0.00
Cloudflare KV (每月 100 万次读取,每月 5 万次写入)$0.00
Cloudflare R2 (2GB 插件资产 + 导出内容存放)$0.03
Cloudflare Durable Objects (每月 40 万次请求)$0.00
Resend (事务性邮件,发送 3 千封)$0.00 (免费额度)
Stripe$0 (无月费,每笔交易 2.9% + 30¢)
总计$0.03/月

预测 1 万 MAU(月活)的业务情况:D1 读取量将放大至每月约 2500 万次(在免费计划环境里依然是 $0),Workers 请求将扩张至每月约 1500 万次(跃迁至 $5/月的 Workers 付费计划),R2 扩至 5GB($0.08)。总体预测得出:于 1 万 MAU 活跃时,各项系统托管开销约为 $5/月——详见 pricing comparison 页面的分类成本明细介绍。

这就是核心结论:在规模较小时,整个插件 + SaaS 的托管技术栈每月成本几乎为零。即使达到 1 万 MAU,成本也不过是两块三明治的钱。托管成本通常不是一款插件产品的瓶颈——真正的瓶颈在于转化率。

代码:端到端的“高亮 → 保存”工作流

要展示这七个模块如何无缝连线的最好而且最为直白的方式,就是看看这款创作者工具产品中的一项核心业务功能流转过程:在任何页面上利用鼠标去选中高亮出一段文本,将其保存到个人知识库,并在 SaaS 控制台上查看它。

typescript
// extension/content-script.ts — 在每个页面上运行
document.addEventListener('mouseup', async () => {
  const selection = window.getSelection()?.toString().trim();
  if (!selection || selection.length < 8) return;

  // 请求 service worker 通过 SaaS API 保存
  const response = await chrome.runtime.sendMessage({
    type: 'SAVE_HIGHLIGHT',
    payload: {
      text: selection,
      url: window.location.href,
      title: document.title,
      capturedAt: Date.now(),
    },
  });

  if (response.ok) {
    showToast('Saved to your knowledge base');
  }
});
typescript
// extension/service-worker.ts — 将弹窗/内容与 SaaS API 桥接
chrome.runtime.onMessage.addListener(async (message, sender) => {
  if (message.type === 'SAVE_HIGHLIGHT') {
    const session = await chrome.storage.session.get('jwt');
    const res = await fetch('https://api.example.com/highlights', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        Authorization: `Bearer ${session.jwt}`,
      },
      body: JSON.stringify(message.payload),
    });
    return { ok: res.ok };
  }
});
typescript
// app/routes/api/highlights.ts — TanStack Start Server Function
import { createServerFileRoute } from '@tanstack/start/server';
import { db } from '~/lib/db';
import { highlights } from '~/lib/schema';
import { auth } from '~/lib/auth';

export const ServerRoute = createServerFileRoute('/api/highlights').methods({
  POST: async ({ request }) => {
    const session = await auth(request);
    if (!session) return new Response('Unauthorized', { status: 401 });

    const body = await request.json();
    const id = crypto.randomUUID();

    await db.insert(highlights).values({
      id,
      userId: session.user.id,
      text: body.text,
      url: body.url,
      title: body.title,
      capturedAt: body.capturedAt,
    });

    return Response.json({ id });
  },
});

这仅仅是一项业务功能流,但它成功跨越了插件端、SaaS API 以及数据库。一共只用到了三个文件。功能代码中看不到身份验证桥接的身影,因为它存放在了一个共享的 lib 中。部署流水线会同时拾取这三个文件,因为它们处于同一个代码仓库 (monorepo) 中。无论是在插件里的请求还是 SaaS 的调用,运行的都是同一个且一致的身份审查验证器——系统中只有一个用户表,一个会话记录以及唯一的事实来源(single source of truth)。

诚实的权衡:这套技术栈在哪些场景不占优势

我不会虚伪地假装这套技术栈在任何产品对比中都能大获全胜。如果你遇到以下三种场景情况,那么你应当考虑去选择一些其他的架构方案:

  • Firefox 优先或 Safari 优先发行。 本文和 TanStack Ship 提供的插件模板仅限于专门对 Chrome MV3 进行支持。到 2026 年 Firefox MV3 已经实现了对应的同等支持,但 Safari 仍然需要单独的 WebExtension 端口以及专门向 App Store 提交应用。如果你的用户群有高达 60%+ 都在使用 Safari 平台,那这意味着你是在自找麻烦,因为你不仅需要被迫维护两套平行的代码库,并而且必须还要同时应对两个不同的商店审查队列。
  • 需要重度本地机器学习计算的插件。 Chrome 内置的 AI (Gemini Nano) 是真实的但也具有很大局限性;如果你的核心特性是在设备终端处理大语言模型 (LLM) 推理,那 MV3 中的 service workers 显然用错了地方。针对此场景下你需要开发一个原生的伴侣应用程序,或者提供基于服务器端的推理路径。
  • 形态仅限于“浏览器优先”并且没有 SaaS 的产品。 如果插件功能涵盖了产品的方方面面,而且你也不需要加入管理团队、计费或者各种数据分析功能,那么在当前这种这里动用七大模块的体系纯属于“杀鸡用牛刀”(overkill)。单纯基于 Vite + Plasmo 的轻型插件项目能够提供更快的发布速度,其后期代码维护起来也更轻量。

若想进一步深入对比分析其他 SaaS 样板体系的情况,请参阅我们的 TanStack Ship vs Makerkit 和 TanStack Ship vs Open SaaS 拆解指南——在这两篇文章中都标明了在哪些业务场景下采用替代方案将会成为赢家。

决策树:选择本技术栈或选择其他方案

选择 TanStack Ship 伴侣产品架构,如果你:

  • 你正致力于在同一款产品中同时交付 Chrome MV3 插件和 SaaS 控制台
  • 你需要在插件与 SaaS 之间实现实时同步
  • 你的计费流程需要依赖 Stripe Checkout、客户门户以及 webhook 具有幂等性响应
  • 你希望两种产品形态共用一套部署流水线
  • 你希望从应用弹窗、到 API 甚至 D1 之间都实现端到端的类型安全

选择纯插件开发框架 (如 Plasmo, WXT),如果你:

  • 该扩展插件就是产品的全部内容;不需要配套任何 SaaS 组件
  • 你希望将开发目标优先针对 Firefox 或 Safari 平台

选择 ShipFast 或 Next.js + Vercel,如果你:

  • SaaS 应用才是产品核心;该插件仅仅是承担吸引流量的营销触点
  • 你的开发团队对 Next.js 非常熟练,而且不打算切换其他技术栈
  • 相比于 Cloudflare 在成本模型上的巨大优势,你更看重和需要 Vercel 提供的开发者体验 (DX)

常见问题解答 (FAQ)

TanStack Ship 是否不仅包含 SaaS 端架构,还包含了 Chrome 扩展插件骨架呢? 两者皆有。在它的启动器代码库里既包含了基于插件目录层的 extension/(内含 MV3 清单、内容脚本、service worker 以及应用弹窗),也同时包含了用于构建 Web 服务的 app/ 目录(TanStack Start SaaS)。这些都被放置在同一个大型代码工程 (monorepo) 内,并在此提供了一个用于存放类型定义和访问数据库的共享 lib/ 文件夹。

我能分开单独部署扩展插件和 SaaS 应用吗? 可以。每当向 main 分支执行代码推送时,SaaS 都会被自动部署到 Cloudflare Workers 上;而扩展插件仅仅会在创建一个带 tag 的发布标签并提交版本时才会部署上架到 Chrome Web Store 商店。这里集成的 GitHub Actions 模板可以完美无瑕的将这两者的不同需求应对且处理完毕。这种借由打 tag 标签进行触发驱动发布的机制极大程度上避免了出现所谓的类似于“扩展插件虽然已经发版更新发布到了 v2.3 但是在云端 SaaS 的版本却依然还停留卡在 v1.5 版本” 这类严重困扰坑害且咬噬那些毫无顾忌就在每次提交时便草率发起一揽子发布的痛心病首的代码漂移 (drift) 现象。

Chrome Web Store 的发布审核期需要花多长时间? 根据我的历史经验来看,平均下来大概需要花费 3 天时间,但是在涉及它的首次上线提交申请时往往会有长达面临 7 天收尾的情况存在。因此请务必相应地合理规划且排布好相关的应用上市发行排期窗口(launch window) —— 有关细节,欢迎查阅我们另外提供的这篇 launch window planning guide 以获取了解更多详情。

那么目前还待在 Chrome Web Store 里面的 MV2 扩展应用会怎样呢? Chrome 早于 2024 年就已经宣布彻底将 MV2 废除,并且打算在一直持续至 2026 年的这段时间内按阶段分批移除当前现有的存量 MV2 应用。千万不要再想着用 MV2 去开启开展你的新产品了。请将使用 MV3 manifest reference 作为你们开发项目的最低下限绝对底线基准 (baseline)。

我能在项目后期再增加针对 Firefox 的开发端口支持吗 (Firefox port)? 完全可以。WXT(被 TanStack Ship 插件模板所使用的底层架构)支持利用相同的一套源代码同时发版对于 Chrome MV3 以及 Firefox MV3 两者双方的支持。在这个当中的唯一难点仅仅是:各类有关图标的问题、应用构建部署清单 (manifest) 存在的异常特性差别、以及专门应付来自 Firefox Add-ons 的等待审核通过队列 —— 这通常也就是让你多付出一周的额外工作时间罢了,而且绝对完全不需要让你面临去对源码进行重新翻写(rewrite)这种修改工作。

身份验证桥接是否支持其他的身份提供商? 随产品提供的桥接是专门针对 Better Auth 而特制的版本。不过将其适配到像 Clerk 或 Auth.js 的方案上也十分直截了当的直接:因为只有 JWT 格式和签名密钥是这里面的唯一耦合点——这通常只需要改写半天时间。

结语

一套 Chrome 插件 + SaaS 伴侣架构是一个垂直领域产品,而不是割裂的两个产品。TanStack Ship 所交付的这七个模块,与任何该产品分类下的生产级构建项目最终必然会编写出来的那七大模块完全相同——这里的样板代码仅仅只是帮你省略隐去了它们之间相互连线的过程而已(collapses the wiring)。总构建连线耗时:为我花掉了 11 天。总体闲置托管成本:$0.03/月。冷启动 p99:50ms。

如果你也正打算在这个季度启动并开始构建一款伴侣产品,那就立刻 clone 一下 TanStack Ship 仓库,敲击运行 pnpm dev 指令,你甚至完全能在享受今天吃午饭前的时间就把那套属于你的 SaaS 仪表盘顺利并且完美的在线路系统内渲染跑起来。因为那七个核心模块,连带整套部署发布流水线链条都已经预先为你打通连接连线就绪了。