Skip to content

Cloudflare Pages

Cloudflare Pages 是面向全栈 Web 应用的边缘平台,既能提供静态文件,也能通过 Cloudflare Workers 输出动态内容。

Hono 对 Cloudflare Pages 提供完整支持。借助 Vite 的极速开发服务器与 Wrangler 的快速部署体验,可以获得十分流畅的开发流程。

1. 环境准备

Cloudflare Pages 提供了现成的模板,可通过 create-hono 命令初始化项目。本示例选择 cloudflare-pages 模板。

sh
npm create hono@latest my-app
sh
yarn create hono my-app
sh
pnpm create hono my-app
sh
bun create hono@latest my-app
sh
deno init --npm hono my-app

进入 my-app 并安装依赖:

sh
cd my-app
npm i
sh
cd my-app
yarn
sh
cd my-app
pnpm i
sh
cd my-app
bun i

基础目录结构如下:

text
./
├── 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 修改为:

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

sh
npm run dev
sh
yarn dev
sh
pnpm dev
sh
bun run dev

4. 部署

如果你拥有 Cloudflare 账号,就可以部署到 Cloudflare。需要注意的是,package.json 中的 $npm_execpath 需改为你使用的包管理器。

sh
npm run deploy
sh
yarn deploy
sh
pnpm run deploy
sh
bun run deploy

通过 Cloudflare 控制台关联 GitHub 部署

  1. 登录 Cloudflare 控制台 并选择你的账号。
  2. 在 Account Home 中,依次选择 Workers & Pages > Create application > Pages > Connect to Git。
  3. 授权 GitHub 账号后选择仓库,在 “Set up builds and deployments” 中填写:
配置项
Production branchmain
Build commandnpm run build
Build directorydist

绑定(Bindings)

Cloudflare Pages 支持变量、KV、D1 等多种绑定。本节以变量与 KV 为例。

创建 wrangler.toml

首先为本地绑定创建 wrangler.toml

sh
touch wrangler.toml

在文件中定义名为 MY_NAME 的变量:

toml
[vars]
MY_NAME = "Hono"

创建 KV

接下来使用 wrangler 命令创建 KV:

sh
wrangler kv namespace create MY_KV --preview

记录命令输出中的 preview_id

{ binding = "MY_KV", preview_id = "abcdef" }

wrangler.toml 中为绑定 MY_KV 指定 preview_id

toml
[[kv_namespaces]]
binding = "MY_KV"
id = "abcdef"

编辑 vite.config.ts

vite.config.ts 更新为:

ts
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 适配器
    }),
    build(),
  ],
})

在应用中使用绑定

在类型定义中加入变量与 KV:

ts
type Bindings = {
  MY_NAME: string
  MY_KV: KVNamespace
}

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

在路由中使用它们:

tsx
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。

客户端脚本

可以借助 Vite 的能力编写并引入客户端脚本。如果 /src/client.ts 是客户端入口,可在模板中按需引用。同时可使用 import.meta.env.PROD 判断当前是否为开发环境。

tsx
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 示例:

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',
        }),
      ],
    }
  }
})

执行以下命令可同时构建服务端与客户端脚本:

sh
vite build --mode client && vite build

Cloudflare Pages 中间件

Cloudflare Pages 拥有与 Hono 不同的中间件机制。在 _middleware.ts 中导出 onRequest 即可启用:

ts
// functions/_middleware.ts
export async function onRequest(pagesContext) {
  console.log(`You are accessing ${pagesContext.request.url}`)
  return await pagesContext.next()
}

借助 handleMiddleware,可以将 Hono 中间件作为 Cloudflare Pages 中间件使用:

ts
// functions/_middleware.ts
import { handleMiddleware } from 'hono/cloudflare-pages'

export const onRequest = handleMiddleware(async (c, next) => {
  console.log(`You are accessing ${c.req.url}`)
  await next()
})

还可以使用 Hono 内置或第三方中间件。例如,为页面添加基础认证可以使用 Hono 的 Basic Auth 中间件

ts
// functions/_middleware.ts
import { handleMiddleware } from 'hono/cloudflare-pages'
import { basicAuth } from 'hono/basic-auth'

export const onRequest = handleMiddleware(
  basicAuth({
    username: 'hono',
    password: 'acoolproject',
  })
)

如果需要应用多个中间件,可以这样写:

ts
import { handleMiddleware } from 'hono/cloudflare-pages'

// ...

export const onRequest = [
  handleMiddleware(middleware1),
  handleMiddleware(middleware2),
  handleMiddleware(middleware3),
]

访问 EventContext

handleMiddleware 中可以通过 c.env 获取 EventContext 对象。

ts
// functions/_middleware.ts
import { handleMiddleware } from 'hono/cloudflare-pages'

export const onRequest = [
  handleMiddleware(async (c, next) => {
    c.env.eventContext.data.user = 'Joe'
    await next()
  }),
]

之后便可在处理函数中通过 c.env.eventContext 读取数据:

ts
// 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)

Released under the MIT License.