把静态博客做出动态能力,在 Cloudflare 上有两条路:Pages Functions(函数文件跟着 Pages 项目一起部署)和独立 Workers(单独一个 Worker 应用)。两条路底层是同一套 Workers 运行时,HTTP 场景下 API 几乎一样,但选错了会在部署耦合、路由规则、额度核算上付出代价。这篇用渡劫博客和渡劫code 一年的真实分工讲清楚怎么选。先说结论:给静态站「打补丁」用 Pages Functions,给产品「开 API」用独立 Workers;新项目建议直接上 Workers Static Assets。

一、两条路分别长什么样

Pages Functions 的写法是「文件即路由」:在 Pages 项目里放 functions/ 目录,文件路径决定路由,导出的 onRequestGet、onRequestPost 处理对应方法:

// functions/api/hello.ts → /api/hello
export const onRequestGet: PagesFunction = async (ctx) => {
  return Response.json({ ok: true, ts: Date.now() });
};

也可以用高级模式:在输出目录放一个 _worker.js,整个 Worker 接管 Pages 的全部请求——先匹配静态资产,再走你的逻辑,相当于「Pages 壳 + 完整 Worker」。

独立 Workers 就是常规 Worker 项目,export default { fetch },自己配路由或自定义域名,部署、版本、日志、回滚全部独立于 Pages,跟静态站没有任何绑定关系。

二、正面 PK:一张能力对照表

按 Cloudflare 官方兼容矩阵(2026 年 10 月核对),关键差异如下:

能力Pages Functions独立 Workers
静态资产托管✅ 原生,免费带宽✅ Static Assets
Cron Triggers❌ 不支持✅ 原生
Queue 消费者❌ 只能生产消息✅ 原生
Durable Objects🟡 类要写在独立 Worker 里✅ 原生
WebSocket🟡 仅高级模式✅ 原生
Workers Logs / Tail / Logpush❌ 有限✅ 完整
灰度发布(Gradual Deployments)❌✅
分支预览部署✅ 原生需 Workers Builds
_redirects / _headers 约定✅ 原生需代码实现

还有个更大的背景:Cloudflare 已官宣 Pages 进入维护模式,新功能(Secrets Store、Workflows 等)全部只上 Workers,官方原话是「把 Pages 的能力逐步变成 Workers 的通用能力」。所以这篇的选型建议带一层时间维度:现有 Pages 项目放心继续用,但新起项目建议直接 Workers Static Assets,一套通吃,避免以后再迁一次。

三、本站的真实分工

渡劫博客主站(CF Pages 端):

  • 静态资产、文章页、列表页、RSS:Pages 静态托管,免费带宽随便造
  • 边缘补丁逻辑:Pages Functions 高级模式做整站 301 重写——旧域名的请求按 Host 判断后跳转到新域(详见 域名迁移实战)
  • 状态页数据接口:functions/status-data.json.ts 读 KV 返回 JSON(见 零成本监控体系)

评论 API(独立 Worker,api.520691.xyz):为什么拆出来?三个理由:

  1. 三端共用:博客三端部署里 Gitee、GitHub 端没有 Functions,只有 CF 端有——API 必须独立于 Pages 存在,三端才能调同一套(架构见 三端同步架构记)
  2. 需要非 HTTP 触发器:评论通知走 Queues 消费者、D1 备份走 Cron,这两样 Pages Functions 都不支持
  3. 部署节奏不同:评论系统一周改三次,不该拖着博客构建一起发布

渡劫code(全套独立 Workers):它本身是个应用不是站——AI Agent 循环跑在 DO 里、终端房间要原生 WebSocket、工作区文件要 R2 直传(见 R2 预签名上传),这些 Pages 要么不支持要么很别扭,没有任何纠结的必要。

四、怎么选:三条判断线

  1. 触发器是不是 HTTP?纯 HTTP 请求才能进 Pages Functions。需要 Cron、Queue 消费、邮件触发——一律独立 Workers,没有第二条路。
  2. 逻辑生命周期跟不跟站点绑定?跟着 git push 一起发布、只服务这个站的逻辑(重定向、header 注入、小表单、SSR 注水),进 Pages;独立演化、给多个前端共用的 API,进 Workers。
  3. 要不要 DO / 原生 WebSocket / 灰度 / 完整日志?要任何一个,进 Workers。Pages 的 DO 玩法要求类声明在独立 Worker 里,等于脱裤子放屁。

五、新项目的默认答案:Workers Static Assets

如果今天从零开始,我会跳过 Pages 直接选 Workers Static Assets——同一个运行时,静态资产 + Worker 逻辑一个项目搞定,Cron、Queue、DO、WebSocket、灰度发布、Workers Logs 全都有,不用再做「Pages + 独立 Worker」的拆分。代价是失去 Pages 的两个原生便利:_redirects/_headers 约定文件(要用代码或 Cloudflare 规则实现)、开箱即用的分支预览(要用 Workers Builds 补)。对个人项目来说,这两个便利远不如「一套通吃」值钱。存量 Pages 项目(比如本站)继续用,Pages 明确说了长期支持,主动迁移是自找麻烦。

六、踩过的坑

  • _worker.js 接管一切:高级模式下静态资产的 404 逻辑、缓存逻辑都要自己写。我们第一版重写脚本写崩了,整站 502,靠 Pages 部署历史一键回滚才恢复—— Pages 的部署历史救了我们,这也是它至今唯一让我舍不得搬去 Workers 的原因。
  • 额度核算要想清楚:Pages 的静态请求不计入 Workers 请求额度(免费带宽),但 Functions 请求计入,免费 10 万/天全账户共享。我们把高 QPS 逻辑放过 Functions 试过,额度烧得比想象快得多——Functions 只放低频边缘逻辑。
  • 部署耦合有体感成本:改一行 Functions 也要等整个 Pages 构建(我们博客构建 40–90 秒)。急性子会被反复折磨,这也是评论 API 拆独立 Worker 的隐藏原因——wrangler deploy 三秒发布。
  • 本地开发命令不一样:Pages 用 wrangler pages dev ./dist,Worker 用 wrangler dev。两个项目的 dev 脚本别混,CI 里更要写清楚。
  • 函数路由大小写敏感、动态段用方括号:functions/api/posts/[slug].ts。从 Next.js 转过来的人容易按文件系统直觉写错。

七、灰度与回滚:被低估的选型因素

独立开发者上线也讲灰度——先让 10% 流量用新代码,错误率不涨再全量。Workers 原生支持 Gradual Deployments,一个 wrangler versions upload 加 wrangler gradual-deployments create 就能按百分比切流,出问题秒级回滚上一个版本。Pages 没有这个能力,它的「灰度」是分支预览部署——能看不能切流,生产环境回滚靠部署历史整个回退。我们评论 Worker 每次涉及鉴权的改动都走灰度,这个能力在 Pages 上要么没有要么很笨。选型时把它和安全边际放在一起掂量:能灰度发布的系统,才敢频繁发布。

小结

Pages Functions 和独立 Workers 不是竞争关系,而是分层:Pages Functions 是「站的边缘层」,独立 Workers 是「产品的 API 层」。技术上所有 HTTP 逻辑两者都能写,分水岭在非 HTTP 能力和部署节奏。再考虑到 Pages 已进入维护模式、投资全部转向 Workers,我的最终建议是:存量 Pages 项目继续用、别折腾迁移;新项目直接 Workers Static Assets;需要定时、队列、DO 的部分,从第一天起就放进独立 Worker。这条线划清楚了,架构至少能少吵一半的架。