Pages Functions 基础
纯静态站点能覆盖博客、文档、营销页这些场景,但一旦要做表单提交、接口代理、动态鉴权,纯静态就不够用了。Pages Functions 给静态站点加上动态能力,让你在同一个项目里既放静态文件,又写服务端接口。这一篇从它的目录约定讲起,覆盖端点编写和动态路由,把基础用法走通。
Pages Functions 是什么
Pages Functions 是 Pages 内置的 Serverless 函数能力。你在项目里建一个 functions 目录,目录里的每个文件就是一个接口端点,Pages 会按文件路径自动生成对应的访问路由。
它运行在 Cloudflare Workers(Cloudflare 的边缘计算函数服务)运行时上,代码在全球边缘节点执行,请求不用绕回中心机房。但不用单独学 Workers 那套开发流程,Pages 把目录约定和路由都封装好了,按文件放对位置就能用。
Functions 和静态文件的关系如下。
| 维度 | 静态文件 | Pages Functions |
|---|---|---|
| 存放位置 | 构建输出目录 | functions 目录 |
| 执行时机 | 构建时生成 | 请求时执行 |
| 内容类型 | 固定的 HTML/CSS/JS | 动态生成的响应 |
| 计费 | 静态请求免费不限量 | 按函数调用次数计费 |
一个 Pages 项目可以同时有静态文件和 Functions,互不冲突。
目录约定
Functions 的路由完全由 functions 目录的文件结构决定,这叫文件路由。目录在哪一层、文件叫什么名,访问路径就对应什么。
假设目录结构如下。
项目根目录/
├── functions/
│ ├── index.js
│ ├── helloworld.js
│ └── api/
│ ├── index.js
│ └── users.js
└── dist/ (静态文件输出目录)
对应的访问路由如下。
| 文件路径 | 访问路由 |
|---|---|
| functions/index.js | / |
| functions/helloworld.js | /helloworld |
| functions/api/index.js | /api |
| functions/api/users.js | /api/users |
两条规律。一是 index.js 代表它所在目录本身,二是文件名就是路径段。functions 目录必须放在项目根目录,不能放到 dist 或 src 里,否则 Pages 识别不到。
文件可以用 .js 或 .ts,TypeScript 会在构建时自动编译。
编写第一个端点
每个端点文件导出一个 onRequest 函数,接收一个 context 对象,返回一个 Response。最简单的示例如下。
// functions/api/hello.js
export function onRequest(context) {
return new Response("Hello from Pages Functions");
}
部署后访问 /api/hello 就能看到这段文字。context 对象包含请求处理需要的所有信息,常用的几个属性如下。
| 属性 | 作用 |
|---|---|
| context.request | 当前的 Request 对象,含请求头、方法、体 |
| context.env | 环境变量和绑定的资源 |
| context.params | 动态路由匹配到的参数 |
| context.next() | 调用下一个处理函数,中间件里用到 |
| context.waitUntil | 注册后台异步任务 |
返回 JSON 接口是更常见的场景,写法如下。
// functions/api/time.js
export async function onRequest(context) {
const data = {
now: new Date().toISOString(),
region: context.request.cf?.colo || "unknown",
};
return new Response(JSON.stringify(data), {
headers: { "Content-Type": "application/json" },
});
}
context.request.cf 是 Cloudflare 注入的请求元信息,colo 是处理这次请求的边缘节点代码,能直观看到边缘计算的就近响应。
按方法分发
实际接口经常要区分 GET、POST、PUT、DELETE。除了用通用的 onRequest,Pages 还支持方法专属的导出名。
| 导出名 | 对应方法 |
|---|---|
| onRequestGet | GET |
| onRequestPost | POST |
| onRequestPut | PUT |
| onRequestDelete | DELETE |
| onRequestPatch | PATCH |
一个文件里同时导出多个方法名,就能让同一个路径响应不同方法。
// functions/api/todos.js
export async function onRequestGet(context) {
return new Response("返回待办列表");
}
export async function onRequestPost(context) {
const body = await context.request.json();
return new Response("创建待办 " + JSON.stringify(body), { status: 201 });
}
访问 /api/todos 时,GET 走 onRequestGet,POST 走 onRequestPost,互不干扰。没导出的方法会返回 405。
动态路由
接口路径里经常带参数,比如 /users/123 里的 123 是用户 ID。Pages 用方括号文件名表示动态参数。
单层参数用一个方括号,文件名 functions/users/[id].js 匹配 /users/任意单段,参数从 context.params 取。
// functions/users/[id].js
export function onRequest(context) {
const id = context.params.id;
return new Response("查询用户 " + id);
}
匹配规则如下。
| 请求路径 | 是否匹配 | params.id |
|---|---|---|
| /users/123 | 是 | 123 |
| /users/abc | 是 | abc |
| /users/123/posts | 否 | |
| /profile/123 | 否 |
单层参数只匹配一段路径,跨段匹配不到。
要匹配任意深度,用双层方括号,文件名 functions/users/[[path]].js 是捕获所有剩余段。
// functions/users/[[path]].js
export function onRequest(context) {
const path = context.params.path;
return new Response(JSON.stringify(path));
}
| 请求路径 | 是否匹配 | params.path |
|---|---|---|
| /users/123 | 是 | 123 |
| /users/123/posts/456 | 是 | [“123”, “posts”, “456”] |
单层参数返回字符串,多层参数返回数组,取值时要注意类型差别。
路由优先级
静态文件和 Functions 同时存在时,谁优先很关键。规则是更具体的路由胜出。
| 路由类型 | 优先级 | 示例 |
|---|---|---|
| 静态文件 | 高 | dist/api/users.json |
| 具名函数文件 | 中 | functions/api/users.js |
| 单层动态参数 | 低 | functions/api/[id].js |
| 多层捕获参数 | 最低 | functions/api/[[rest]].js |
也就是说,如果 dist/api/users.json 和 functions/api/users.js 同时存在,静态文件赢,函数不会执行。设计接口时要避开和静态文件路径冲突。
本地开发
每次改完都推到 Git 等部署再调试太慢。Pages 提供本地开发命令,用 Wrangler 在本地起一个和线上一样的环境。
npx wrangler pages dev ./dist
这条命令以 dist 为静态文件目录启动本地服务,默认地址 http://localhost:8788。它会自动识别 functions 目录并挂载,改完代码热重载,请求直接打到本地函数。
本地开发还能模拟环境变量和绑定。
npx wrangler pages dev ./dist --var API_KEY:local-test-key
本地跑通再推送,能省下大量看构建日志的时间。
Pages Functions 的核心是文件路由。functions 目录的文件结构就是接口路径,index.js 代表目录本身,[param] 是单层动态参数,[[param]] 是多层捕获。每个端点导出 onRequest 或方法专属函数,用 context 拿请求信息并返回 Response。下一篇讲中间件,给一组接口统一加上鉴权、日志、A/B 测试这类公共逻辑。
上一篇 自定义域名与重定向规则
下一篇 Pages Functions 中间件