把静态博客做出动态能力,在 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):为什么拆出来?三个理由:
- 三端共用:博客三端部署里 Gitee、GitHub 端没有 Functions,只有 CF 端有——API 必须独立于 Pages 存在,三端才能调同一套(架构见 三端同步架构记)
- 需要非 HTTP 触发器:评论通知走 Queues 消费者、D1 备份走 Cron,这两样 Pages Functions 都不支持
- 部署节奏不同:评论系统一周改三次,不该拖着博客构建一起发布
渡劫code(全套独立 Workers):它本身是个应用不是站——AI Agent 循环跑在 DO 里、终端房间要原生 WebSocket、工作区文件要 R2 直传(见 R2 预签名上传),这些 Pages 要么不支持要么很别扭,没有任何纠结的必要。
四、怎么选:三条判断线
- 触发器是不是 HTTP?纯 HTTP 请求才能进 Pages Functions。需要 Cron、Queue 消费、邮件触发——一律独立 Workers,没有第二条路。
- 逻辑生命周期跟不跟站点绑定?跟着 git push 一起发布、只服务这个站的逻辑(重定向、header 注入、小表单、SSR 注水),进 Pages;独立演化、给多个前端共用的 API,进 Workers。
- 要不要 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。这条线划清楚了,架构至少能少吵一半的架。