EdgeOne Makers 从入门到精通
附录

§11 边缘函数 Pages Functions:在离用户最近的地方算

从"能跑"到"能打"。读完这 Part,你能用边缘函数、缓存策略、CLI 流水线和定时任务,把 Agent 与 Web 服务做成生产级工程,而不只是又一个 demo。

11.1 我为什么不再自己买服务器

2月前我接了个需求:给一个活动页加"实时倒计时 + 区域限流"。第一反应是自己开台 CVM,写个 Go 服务。算下来:机器钱、运维、扩容、被攻击的担心。我后来想,这种"请求来了算一下就返回"的逻辑,凭什么要我养一台服务器?

Makers 的边缘函数正好吃这个场景。

11.2 它怎么把代码铺到 3200+ 节点

先给结论:Pages Functions 运行在 EdgeOne 3200+ 边缘节点上,你的代码不用落在一台固定服务器,而是被调度到离用户最近的节点执行。

维度 自建服务器 Pages Functions 标叔的结论
部署位置 单区域一台 3200+ 边缘节点 就近响应
扩容 自己加机器 平台弹性 不用操心峰值
延迟 跨地域高 就近低 体验直接好
运维 自己盯 平台管 省一个人
重点看
最后一列"运维"。边缘函数把"机器"这件事从你的待办里删了。

11.3 路由就是目录:/functions 约定优于配置

Makers 不让你写路由表。你在项目根目录建一个 functions/ 文件夹,里面每个 .js 文件自动变成一条路由。

// functions/index.js          ->  example.com/
// functions/hello-pages.js    ->  example.com/hello-pages
// functions/api/users/list.js ->  example.com/api/users/list

文件结构即路由表。没匹配到的请求,自动回退到静态资源。

标叔的经验
约定优于配置,省的是你维护路由文件的心智。新手最容易犯的错,是去翻"路由配置在哪",其实根本没那个东西。

11.4 动态路由:[id] 与 [[default]]

真实 API 不可能每个 ID 写一个文件。Makers 支持两种动态段:

// functions/api/users/[id].js      ->  匹配 /api/users/1024(单级)
// functions/api/[[default]].js     ->  匹配 /api/books/list(多级 catch-all)
// functions/api/users/[id].js
export function onRequestGet({ params }) {
  return new Response(`用户 ID 是 ${params.id}`);
}

注意:[id] 只吃一级路径,/api/users/vip/1024 它接不住;[[default]] 能吞多级。选错会 404,我踩过。

11.5 Handlers:按 HTTP 方法分派

一个文件能同时处理多种方法,也能只认一种:

export function onRequest(context) { /* 所有方法 */ }
export function onRequestGet(context) { /* 只认 GET */ }
export function onRequestPost(context) { /* 只认 POST */ }
// 还有 Put / Delete / Patch / Head / Options

context 里你能拿到:request(请求对象)、params(动态路由参数)、env(环境变量)。

警告:边缘函数不支持 addEventListener,别拿传统 Service Worker 那套来写。它只认 Function Handlers。我第一次写就栽在这。

11.6 三个真实示例:geo / KV / supabase

(1)拿客户端地理位置——context.request.eo.geo 直接给你:

export function onRequest({ request }) {
  const geo = request.eo.geo;
  return new Response(JSON.stringify({ geo }), {
    headers: { 'content-type': 'application/json; charset=UTF-8' },
  });
}

(2)用 KV 存访问数——my_kv 是你在项目里绑定的命名空间变量名:

export async function onRequest({ env }) {
  const n = Number(await my_kv.get('visitCount')) + 1;
  await my_kv.put('visitCount', String(n));
  return new Response(JSON.stringify({ visitCount: n }));
}

(3)连三方数据库 supabase

import { createClient } from '@supabase/supabase-js';
export async function onRequest({ env }) {
  const supabase = createClient(env.supabaseUrl, env.supabaseKey);
  const { data } = await supabase.from('users').select('*');
  return new Response(JSON.stringify({ users: data }));
}

标叔的建议:示例三连三方库,记得把依赖装进项目,别以为边缘节点自带。详见 §13 的环境变量与 §14 的 includeFiles。

11.7 边缘函数不是银弹

它跑的是 V8、ES6、标准 Web API(Fetch / Cache / Crypto / Stream 都有)。但单函数有运行时长上限,重计算别往里塞。

能在边缘算的"轻逻辑"——鉴权、改写、限流、拼装——用它;要算半天的重活,留给云函数或你的后端。

下一章,我们让这些静态资源和函数响应"快到极致"。