Skip to content

JSX 渲染器中间件

JSX 渲染器中间件让你能够在使用 c.render() 函数渲染 JSX 时设置布局,而无需使用 c.setRenderer()。此外,它还允许通过 useRequestContext() 在组件中访问 Context 实例。

导入

ts
import { Hono } from 'hono'
import { jsxRenderer, useRequestContext } from 'hono/jsx-renderer'

使用方法

jsx
const app = new Hono()

app.get(
  '/page/*',
  jsxRenderer(({ children }) => {
    return (
      <html>
        <body>
          <header>菜单</header>
          <div>{children}</div>
        </body>
      </html>
    )
  })
)

app.get('/page/about', (c) => {
  return c.render(<h1>关于我!</h1>)
})

配置选项

可选 docType: boolean | string

如果你不想在 HTML 开头添加 DOCTYPE 声明,可以将 docType 选项设置为 false

tsx
app.use(
  '*',
  jsxRenderer(
    ({ children }) => {
      return (
        <html>
          <body>{children}</body>
        </html>
      )
    },
    { docType: false }
  )
)

你也可以指定自定义的 DOCTYPE。

tsx
app.use(
  '*',
  jsxRenderer(
    ({ children }) => {
      return (
        <html>
          <body>{children}</body>
        </html>
      )
    },
    {
      docType:
        '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">',
    }
  )
)

可选 stream: boolean | Record<string, string>

如果将其设置为 true 或提供一个 Record 值,响应将以流式方式渲染。

tsx
const AsyncComponent = async () => {
  await new Promise((r) => setTimeout(r, 1000)) // 暂停 1 秒
  return <div>你好!</div>
}

app.get(
  '*',
  jsxRenderer(
    ({ children }) => {
      return (
        <html>
          <body>
            <h1>SSR 流式渲染</h1>
            {children}
          </body>
        </html>
      )
    },
    { stream: true }
  )
)

app.get('/', (c) => {
  return c.render(
    <Suspense fallback={<div>加载中...</div>}>
      <AsyncComponent />
    </Suspense>
  )
})

如果设置为 true,将添加以下响应头:

ts
{
  'Transfer-Encoding': 'chunked',
  'Content-Type': 'text/html; charset=UTF-8',
  'Content-Encoding': 'Identity'
}

你可以通过指定 Record 值来自定义响应头的值。

嵌套布局

Layout 组件支持布局的嵌套。

tsx
app.use(
  jsxRenderer(({ children }) => {
    return (
      <html>
        <body>{children}</body>
      </html>
    )
  })
)

const blog = new Hono()
blog.use(
  jsxRenderer(({ children, Layout }) => {
    return (
      <Layout>
        <nav>博客菜单</nav>
        <div>{children}</div>
      </Layout>
    )
  })
)

app.route('/blog', blog)

useRequestContext()

useRequestContext() 返回一个 Context 实例。

tsx
import { useRequestContext, jsxRenderer } from 'hono/jsx-renderer'

const app = new Hono()
app.use(jsxRenderer())

const RequestUrlBadge: FC = () => {
  const c = useRequestContext()
  return <b>{c.req.url}</b>
}

app.get('/page/info', (c) => {
  return c.render(
    <div>
      你正在访问:<RequestUrlBadge />
    </div>
  )
})

WARNING

你不能在 Deno 的 precompile JSX 选项中使用 useRequestContext()。请使用 react-jsx

json
   "compilerOptions": {
     "jsx": "precompile", 
     "jsx": "react-jsx", 
     "jsxImportSource": "hono/jsx"
   }
 }

扩展 ContextRenderer

通过如下方式定义 ContextRenderer,你可以向渲染器传递额外的内容。这在需要根据页面更改 head 标签内容等场景下特别有用。

tsx
declare module 'hono' {
  interface ContextRenderer {
    (
      content: string | Promise<string>,
      props: { title: string }
    ): Response
  }
}

const app = new Hono()

app.get(
  '/page/*',
  jsxRenderer(({ children, title }) => {
    return (
      <html>
        <head>
          <title>{title}</title>
        </head>
        <body>
          <header>菜单</header>
          <div>{children}</div>
        </body>
      </html>
    )
  })
)

app.get('/page/favorites', (c) => {
  return c.render(
    <div>
      <ul>
        <li>吃寿司</li>
        <li>看棒球比赛</li>
      </ul>
    </div>,
    {
      title: '我的最爱',
    }
  )
})

Released under the MIT License.