发布于: -/最后更新: -/6 分钟/

Cloudflare Workers 上手:从零到部署一个短链接服务

摘要

Cloudflare Workers 是一个无服务器平台,允许开发者在全球边缘网络上运行代码。它支持多种语言,包括 TypeScript、JavaScript、Python 和 Rust。Workers 的特点是无状态,意味着数据无法持久化,但 Cloudflare 提供了一整套配套服务,包括 KV、D1、R2、Queues 和 Workflows,来帮助开发者构建应用。开发者可以使用 Wrangler 配置文件定义 Worker 需要的资源,并使用 Hono 框架简化路由处理。通过 Cloudflare Workers,开发者可以快速构建全球可用的应用,并享受低延迟和高性能的优势。

Cloudflare Workers 是一个无服务器(Serverless)平台。用它构建的应用可以享受 Cloudflare 遍布全球的边缘网络——你的代码会在离用户最近的节点运行。

它支持 TypeScript、JavaScript、Python、Rust 等语言(当然,大部分人都用 TS/JS)。

无服务器意味着什么?

无服务器 = 无状态(Stateless)。

好处是可扩展性极强:请求来了,Cloudflare 会在全球各个节点瞬间启动实例来处理。底层用的是 V8 引擎,冷启动飞快。请求处理完,实例就休眠了。

限制是数据无法持久化——但这不是问题,Cloudflare 提供了一整套配套服务:

服务

用途

KV

键值对存储(本文会用到)

D1

SQLite 数据库

R2

对象存储(S3 兼容)

Queues

异步任务队列

Workflows

持久化工作流

Workers AI

AI 推理

再加上开箱即用的日志和可观测性,基本上什么类型的应用都能构建了。

今天我们就来上手一下:写一个简单的 React 应用,再加一个短链接生成功能,最后部署上去。

前提:一个 Cloudflare 账号 + Node 环境。

创建项目

Bash
pnpm create cloudflare@latest my-react-app --framework=react

一路回车即可。

项目结构概览

Plain Text
my-react-app/
├── src/                   # React 前端源代码
│   ├── App.tsx
│   ├── main.tsx
│   └── ...
├── worker/                # Cloudflare Workers 后端代码
│   └── index.ts           # Worker 入口
├── wrangler.jsonc         # 核心配置文件(部署、资源绑定)
├── worker-configuration.d.ts  # 自动生成的类型定义
├── vite.config.ts
├── package.json
└── tsconfig.*.json

重点关注两个地方:

  • worker/ 目录:后端逻辑

  • wrangler.jsonc:基础设施即代码(Infrastructure as Code)

核心配置:wrangler.jsonc

这个文件定义了你的 Worker 需要什么资源。部署时 Cloudflare 会根据它来分配资源。

JSON
{
  "$schema": "node_modules/wrangler/config-schema.json",
  "name": "my-react-app",
  "main": "worker/index.ts",
  "compatibility_date": "2025-09-27",
  "assets": {
    "not_found_handling": "single-page-application"
  },
  "observability": {
    "enabled": true
  }
}

Worker 入口

TypeScript
// worker/index.ts
export default {
  fetch(request) {
    const url = new URL(request.url);

    if (url.pathname.startsWith("/api/")) {
      return Response.json({ name: "Cloudflare" });
    }

    return new Response(null, { status: 404 });
  },
} satisfies ExportedHandler<Env>;

就是一个 fetch handler,你来决定怎么处理请求。模板代码处理了 /api/ 路径,返回一段 JSON,其余返回 404。

类型生成

worker-configuration.d.ts 是根据 wrangler.jsonc 自动生成的类型文件,让 TypeScript 能识别你绑定的资源(KV、D1、环境变量等)。

每次改完配置文件,运行一下:

Bash
pnpm cf-typegen

跑起来

Bash
pnpm dev

打开页面,点击按钮,就能看到从后端接口返回的 "Cloudflare"

实战:短链接生成器

接下来整点实际的——用 KV 存储做一个短链接服务。

绑定 KV 资源

修改 wrangler.jsonc

JSON
{
  "$schema": "node_modules/wrangler/config-schema.json",
  "name": "my-react-app",
  "main": "worker/index.ts",
  "compatibility_date": "2025-09-27",
  "assets": {
    "not_found_handling": "single-page-application",
    "run_worker_first": ["/s/*", "/api/*"]
  },
  "observability": {
    "enabled": true
  },
  "kv_namespaces": [
    {
      "binding": "KV"
    }
  ]
}

两个关键改动:

  1. run_worker_first:指定哪些路径由 Worker 处理,否则会被前端 SPA 接管

  2. kv_namespaces:绑定一个 KV 命名空间,变量名叫 KV

然后重新生成类型:

Bash
pnpm cf-typegen

安装 Hono

手写 if/else 路由太累了,装个轻量级框架:

Bash
pnpm add hono

编写后端逻辑

TypeScript
// worker/hono.ts
import { Hono } from "hono";

export const app = new Hono<{ Bindings: Env }>();

// 生成短链接
app.post("/api/set-link", async (c) => {
  const body = await c.req.json<{ link: string }>();
  let link = body.link;

  // 确保链接以 http(s) 开头
  if (!/^https?:\/\//i.test(link)) {
    link = "https://" + link;
  }

  // 生成 6 位随机 ID
  const id = Math.random().toString(36).substring(2, 8);

  // 写入 KV
  await c.env.KV.put(id, link);

  return c.json({ id });
});

// 短链接跳转
app.get("/s/:id", async (c) => {
  const id = c.req.param("id");
  const link = await c.env.KV.get(id);

  if (!link) {
    return c.json({ error: "Link not found" }, 404);
  }

  return c.redirect(link);
});

通过 c.env.KV 就能访问绑定的 KV 资源,类型提示完美。

修改入口文件

TypeScript
// worker/index.ts
import { app } from "./hono";

export default {
  fetch(request, env, ctx) {
    return app.fetch(request, env, ctx);
  },
} satisfies ExportedHandler<Env>;

前端页面

😋让AI简单写个输入框和按钮及样式:

CSS
/* App.css */
#root {
  max-width: 1280px;
  margin: 0 auto;
  padding: 2rem;
  text-align: center;
  font-family: system-ui, -apple-system, sans-serif;
}

.container {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2rem;
  margin-top: 10vh;
}

h1 {
  font-size: 2.5rem;
  margin-bottom: 1rem;
}

.input-group {
  display: flex;
  gap: 10px;
  width: 100%;
  max-width: 600px;
}

input {
  flex: 1;
  padding: 12px;
  border-radius: 8px;
  border: 1px solid #444;
  background: #2a2a2a;
  color: white;
  font-size: 1rem;
}

button {
  padding: 12px 24px;
  border-radius: 8px;
  border: 1px solid transparent;
  background-color: #646cff;
  color: white;
  cursor: pointer;
  font-weight: bold;
  font-size: 1rem;
  transition: background-color 0.25s;
}

button:hover {
  background-color: #535bf2;
}

.result {
  margin-top: 2rem;
  padding: 2rem;
  background: #1a1a1a;
  border-radius: 12px;
  width: 100%;
  max-width: 500px;
}

.link-box {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 1rem;
  margin-top: 1rem;
  padding: 1rem;
  background: #000;
  border-radius: 8px;
}

a {
  color: #646cff;
  text-decoration: none;
  word-break: break-all;
}

a:hover {
  text-decoration: underline;
}
TSX
// src/App.tsx
import { useState } from 'react'
import './App.css'

function App() {
  const [inputUrl, setInputUrl] = useState('')
  const [shortLink, setShortLink] = useState('')
  const [loading, setLoading] = useState(false)

  const handleShorten = async () => {
    if (!inputUrl) return;
    setLoading(true);
    try {
      const res = await fetch('/api/set-link', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ link: inputUrl })
      });
      const data = await res.json() as { id: string };
      const protocol = window.location.protocol;
      const host = window.location.host;
      setShortLink(`${protocol}//${host}/s/${data.id}`);
    } catch (e) {
      console.error(e);
      alert('Failed to shorten link');
    } finally {
      setLoading(false);
    }
  };

  return (
    <div className="container">
      <h1>短链接生成器</h1>
      <div className="input-group">
        <input
          type="url"
          placeholder="输入长链接..."
          value={inputUrl}
          onChange={(e) => setInputUrl(e.target.value)}
          onKeyDown={(e) => e.key === 'Enter' && handleShorten()}
        />
        <button onClick={handleShorten} disabled={loading}>
          {loading ? '...' : '生成短链接'}
        </button>
      </div>
      
      {shortLink && (
        <div className="result">
          <p>短链接:</p>
          <div className="link-box">
            <a href={shortLink} target="_blank" rel="noopener noreferrer">{shortLink}</a>
            <button onClick={() => navigator.clipboard.writeText(shortLink)}>
              复制
            </button>
          </div>
        </div>
      )}
    </div>
  )
}

export default App

本地 pnpm dev 测试一下,功能正常就可以部署了。

部署

一条命令:

Bash
pnpm run deploy

第一次会让你登录授权。然后你会看到类似这样的输出:

Plain Text
✨ Read 5 files from the assets directory
✨ Success! Uploaded 4 files (1.22 sec)

Provisioning KV (KV Namespace)...
🌀 Creating new KV Namespace "my-react-app-kv"...
✨ KV provisioned 🎉

Deployed my-react-app triggers (1.26 sec)
  https://my-react-app.xxxxx.workers.dev

Cloudflare 会自动帮你创建 KV 资源,并把生成的 ID 写回 wrangler.jsonc

JSON
"kv_namespaces": [
  {
    "binding": "KV",
    "id": "62ff346972fe40d3b23cebb679ea4d68"
  }
]

在 wrangler 4.45.0 之前,你得先手动创建 KV、拿到 ID、填进配置文件。现在不用了,部署时自动搞定。

打开分配的 *.workers.dev 域名,你的短链接服务就上线了。

小结

Cloudflare Workers 的开发体验相当丝滑:

  1. 本地开发pnpm dev,热更新,KV 也能本地模拟

  2. 类型安全wrangler types 自动生成绑定类型

  3. 一键部署pnpm deploy,资源自动创建

如果你想快速搭建一个全球可用的小应用,Workers 是个很好的选择。

正文结束