这一章全是中文讲解。代码里的英文单词是固定写法,照着抄就行,每个关键地方都配了中文说明。带「运行」按钮的代码框,点一下就能在手机上看到真实结果。
学完这一章,你就能看懂自己 tg-bot 那类 Worker 的骨架了:为什么它不用开服务器,为什么换个路径就能干不同的事,为什么改了绑定名就读不到数据。
没有服务器、没有端口。你不用租一台机器守在那里。CF(Cloudflare)把你的函数部署在全球的边缘节点上——离访客最近的地方——有人来请求,就近跑你的代码,跑完就结束。
一个 Worker 最少长这样。记住两个入口:fetch 管「来请求了」,scheduled 管「到点了」。
export default {
// 有请求进来时,跑这个函数
async fetch(request, env, ctx) {
return new Response("Hello!", { status: 200 });
},
// 定时触发:到点自动调用这个
// 干刷新缓存、发提醒这类活
async scheduled(event, env, ctx) {
// 到点要做的事写在这里
}
};
request:进来的请求——对方要什么、带了什么,都在里面。env:绑定和密钥——你的 KV、数据库、密码,都从这里进。ctx:杂务工具——比如「先回响应、后台再慢慢干活」这类事用它。请求进来后,你能从 request 上拿到这些信息:
request.method —— 请求方法,是 GET(取东西)还是 POST(交东西)等。request.url —— 完整网址字符串。先用 new URL(request.url) 把它拆开,下面几项都是拆开后取的。url.pathname —— 路径部分,如 /api/user。路由判断靠它(第 ⑦ 点)。url.searchParams.get("q") —— 查询参数的值。网址是 ?q=你好 时,这里取到的就是「你好」。request.headers.get("Authorization") —— 某个请求头的值,常用来取对方带来的令牌。await request.json() / .text() / .formData() —— 按格式读请求体:JSON 用第一个,纯文字用第二个,表单用第三个。前面加 await 是因为读取要等一下。request.cf —— CF 附加的信息:访客的国家、IP 所在城市等。处理完,就要造一个 Response(响应)返回。最常用的四种:
// 1)纯文本:最简单
return new Response("ok");
// 2)JSON:做接口(API)时的标准返回法
return new Response(
JSON.stringify({ ok: true }),
{ headers: { "Content-Type": "application/json" } }
);
// 3)跳转:把访客送到另一个网址,302 是临时跳转
return Response.redirect("https://example.com", 302);
// 4)反向代理:把请求转发给上游地址,原样返回它的结果
return fetch("https://上游地址" + url.pathname);
env 是第二个参数。你在 CF 后台绑定的东西,全部挂在它上面,代码里从这里取:
KV 命名空间 —— 想象成一个全网的「键 → 值」大本子。假设绑定名叫 MY_KV:
await env.MY_KV.get("键"); // 读
await env.MY_KV.put("键", "值"); // 写
await env.MY_KV.delete("键"); // 删
await env.MY_KV.get("键", "json"); // 直接按 JSON 读出来
D1 数据库 —— 正经的表格数据库,绑定名假设叫 DB:
// 查一行:? 是占位符,真正的值用 bind 填,防注入
await env.DB.prepare("SELECT * FROM t WHERE id=?")
.bind(id).first();
// .all() 查多行;.run() 执行写操作(增、删、改)
await env.DB.prepare("SELECT * FROM t").all();
env.ADMIN_TOKEN:和普通字符串一样读,但在后台要设为 Secret 类型——代码里看不到明文,别人翻代码也偷不走。env.XXX:不保密的配置项,比如开关、提示语,放这里方便改,不用动代码。坑一:绑定名必须一字不差。代码里写 env.Senflare,后台绑的却是别的名字,那 env.Senflare 就是 undefined(空的、不存在),后面一读就出问题。名字对不上,程序不会自己去猜。
坑二:重传代码会冲掉绑定。用某些部署工具重新上传代码时,会把你在后台手动加的绑定一起冲掉。记住顺序:部署后先验证绑定还在,再验证功能。
同一个 Worker,靠判断 url.pathname 给不同路径干不同的事,这就叫路由:
async fetch(request, env) {
const url = new URL(request.url);
if (url.pathname === "/") {
return new Response("首页");
}
if (url.pathname === "/api/time") {
return Response.json({ time: new Date().toISOString() });
}
// 都不匹配:返回 404,意思是「没这个页面」
return new Response("Not Found", { status: 404 });
}
下面三段是纯 JavaScript,浏览器里就能跑,点「运行」看真实结果。Workers 专属的部分(export default、env、KV)浏览器跑不了,放在后面只看不跑,并标了「在 Workers 里跑」。
把一个完整网址交给 new URL(...) 拆开,看路径和查询参数分别是什么。
const url = new URL("https://example.com/api/time?q=1");
console.log("完整网址 href:", url.href);
console.log("路径 pathname:", url.pathname);
console.log("查询参数 q:", url.searchParams.get("q"));
用一个普通函数模拟 fetch 里的路由逻辑,给它三条路径,看每条各返回什么。
// 模拟路由:只看路径,决定回什么
function route(pathname) {
if (pathname === "/") return "首页";
if (pathname === "/api/time") return "时间接口的数据";
return "404 Not Found(没这个页面)";
}
console.log("/ →", route("/"));
console.log("/api/time →", route("/api/time"));
console.log("/nope →", route("/nope"));
Response 浏览器里也有。造好之后再把它的文本读出来,看看 JSON 响应长什么样。
const res = new Response(
JSON.stringify({ ok: true }),
{ headers: { "Content-Type": "application/json" } }
);
console.log("状态码 status:", res.status);
console.log("内容类型:", res.headers.get("Content-Type"));
const text = await res.text();
console.log("响应文本:", text);
export default 是把整个 Worker 交出去的写法,只有 Workers 环境认它,浏览器里跑不了,记住形状就行。
export default {
async fetch(request, env, ctx) {
return new Response("Hello!", { status: 200 });
}
};
env.MY_KV 是后台绑进来的,浏览器里没有这个东西,所以这段只能在 Workers 里跑。
await env.MY_KV.put("count", "1"); // 写:存进去
const v = await env.MY_KV.get("count"); // 读:取出来,v 是字符串 "1"
await env.MY_KV.delete("count"); // 删:抹掉
共 5 题。点一下选项,立刻判对错:选对了会讲为什么对,选错了会讲错在哪。
你的 Telegram 机器人就是一个 Worker:Telegram 把消息以请求的形式发进来,fetch 接住、处理、回话;每天的到期提醒、定时提醒,则靠 scheduled 到点自动跑。一个 fetch + 一个 scheduled,就是它的全部骨架。
不用为每个功能部署一个 Worker:/ 返回首页,/api/time 返回时间接口,其他没登记的路径统一回 404。判断的依据就是 url.pathname——上面「试一试 2」里模拟的正是这套逻辑。
第一,绑定名一字不差:代码写 env.Senflare,后台就必须绑这个名,差一个字母就是 undefined。第二,重传冲绑定:部署工具重传代码可能冲掉后台手加的绑定。所以每次部署后,顺序永远是先验绑定还在不在,再验功能好不好。
下一章:模块文件格式 →