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-app
npm i
yarn
cd my-app
yarn
pnpm
cd my-app
pnpm i
bun
cd my-app
bun 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: string
MY_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.ts
export 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.ts
export const onRequest = [
handleMiddleware(async (c, next) => {
c.env.eventContext.data.user = 'Joe'
await next()
}),
]
在 API handler 中:
// functions/api/[[route]].ts
import 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)