Cloudflare Pages
Cloudflare Pages 是一个用于全栈 Web 应用的边缘平台。它既可以提供静态文件,也可以通过 Cloudflare Workers 提供动态内容。
Hono 完全支持 Cloudflare Pages,带来非常优秀的开发体验。Vite 的开发服务器非常快速,而通过 Wrangler 部署也极其便捷。
1. 安装
Cloudflare Pages 提供了 Starter 模板。使用 create-hono 命令启动项目,本例选择 cloudflare-pages 模板。
npm
npm create hono@latest my-app
yarn
yarn create hono my-app
pnpm
pnpm create hono@latest my-app
bun
bun create hono@latest my-app
deno
deno init --npm hono my-app
进入 my-app 目录并安装依赖。
npm
cd my-appnpm i
yarn
cd my-appyarn
pnpm
cd my-apppnpm i
bun
cd my-appbun i
下面是一个基本目录结构:
./├── package.json├── public│ └── static // 放置静态文件│ └── style.css // 通过 `/static/style.css` 引用├── src│ ├── index.tsx // 服务端入口文件│ └── renderer.tsx├── tsconfig.json└── vite.config.ts
2. Hello World
编辑 src/index.tsx:
import { Hono } from 'hono'import { renderer } from './renderer'const app = new Hono()app.get('*', renderer)app.get('/', (c) => {return c.render(<h1>Hello, Cloudflare Pages!</h1>)})export default app
3. 运行
本地运行开发服务器,然后访问 http://localhost:5173。
npm
npm run dev
yarn
yarn dev
pnpm
pnpm dev
bun
bun run dev
4. 部署
如果有 Cloudflare 账号,可以部署到 Cloudflare。记得在 package.json 中将 $npm_execpath 改为你的包管理器。
npm
npm run deploy
yarn
yarn deploy
pnpm
pnpm run deploy
bun
bun run deploy
通过 Cloudflare 控制台 + GitHub 部署
- 登录 Cloudflare dashboard,选择账号。
- 在 Account Home 中,进入 Workers & Pages > Create application > Pages > Connect to Git。
- 授权 GitHub 账号并选择仓库,在构建配置中填写:
| 配置项 | 值 |
|---|---|
| Production branch | main |
| Build command | npm run build |
| Build directory | dist |
Bindings
你可以使用 Cloudflare Bindings,如 Variables、KV、D1 等。以下例子展示了如何使用 Variables 和 KV。
创建 wrangler.toml
首先,为本地 Bindings 创建 wrangler.toml:
touch wrangler.toml
编辑 wrangler.toml,添加变量 MY_NAME:
[vars]MY_NAME = "Hono"
创建 KV
创建 KV 命名空间:
wrangler kv namespace create MY_KV --preview
记录输出的 preview_id,并在配置中指定:
[[kv_namespaces]]binding = "MY_KV"id = "abcdef"
编辑 vite.config.ts
配置 Vite:
import devServer from '@hono/vite-dev-server'import adapter from '@hono/vite-dev-server/cloudflare'import build from '@hono/vite-cloudflare-pages'import { defineConfig } from 'vite'export default defineConfig({plugins: [devServer({entry: 'src/index.tsx',adapter, // Cloudflare Adapter}),build(),],})
在应用中使用 Bindings
type Bindings = {MY_NAME: stringMY_KV: KVNamespace}const app = new Hono<{ Bindings: Bindings }>()
示例代码:
app.get('/', async (c) => {await c.env.MY_KV.put('name', c.env.MY_NAME)const name = await c.env.MY_KV.get('name')return c.render(<h1>Hello! {name}</h1>)})
生产环境配置
在 Cloudflare Pages 上,wrangler.toml 只用于本地开发,生产环境需在控制台配置 Bindings。
Client-side
你可以编写客户端脚本并通过 Vite 导入。例如 /src/client.ts 是客户端入口:
app.get('/', (c) => {return c.html(<html><head>{import.meta.env.PROD ? (<script type='module' src='/static/client.js'></script>) : (<script type='module' src='/src/client.ts'></script>)}</head><body><h1>Hello</h1></body></html>)})
使用如下 vite.config.ts:
import pages from '@hono/vite-cloudflare-pages'import devServer from '@hono/vite-dev-server'import { defineConfig } from 'vite'export default defineConfig(({ mode }) => {if (mode === 'client') {return {build: {rollupOptions: {input: './src/client.ts',output: {entryFileNames: 'static/client.js',},},},}} else {return {plugins: [pages(),devServer({entry: 'src/index.tsx',}),],}}})
构建客户端与服务端:
vite build --mode client && vite build
Cloudflare Pages Middleware
Cloudflare Pages 使用独立的 middleware 系统,与 Hono 的 middleware 不同。你可以在 _middleware.ts 中导出 onRequest:
// functions/_middleware.tsexport async function onRequest(pagesContext) {console.log(`You are accessing ${pagesContext.request.url}`)return await pagesContext.next()}
使用 handleMiddleware 可以让 Hono 的 middleware 在 Pages 中生效:
import { handleMiddleware } from 'hono/cloudflare-pages'export const onRequest = handleMiddleware(async (c, next) => {console.log(`You are accessing ${c.req.url}`)await next()})
使用多重 Middleware
export const onRequest = [handleMiddleware(middleware1),handleMiddleware(middleware2),handleMiddleware(middleware3),]
访问 EventContext
可以通过 c.env 获取 EventContext 对象:
// functions/_middleware.tsexport const onRequest = [handleMiddleware(async (c, next) => {c.env.eventContext.data.user = 'Joe'await next()}),]
在 API handler 中:
// functions/api/[[route]].tsimport type { EventContext } from 'hono/cloudflare-pages'import { handle } from 'hono/cloudflare-pages'type Env = {Bindings: {eventContext: EventContext}}const app = new Hono<Env>().basePath("/api")app.get('/hello', (c) => {return c.json({message: `Hello, ${c.env.eventContext.data.user}!`, // 'Joe'})})export const onRequest = handle(app)
