Skip to content

JSX 渲染器中间件

🌐 JSX Renderer Middleware

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

🌐 JSX Renderer Middleware allows you to set up the layout when rendering JSX with the c.render() function, without the need for using c.setRenderer(). Additionally, it enables access to instances of Context within components through the use of useRequestContext().

导入

🌐 Import

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

用法

🌐 Usage

jsx
const app = new Hono()

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

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

选项

🌐 Options

optional 文档类型: boolean | string

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

🌐 If you do not want to add a DOCTYPE at the beginning of the HTML, set the docType option to false.

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

你可以指定 DOCTYPE。

🌐 And you can specify the 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">',
    }
  )
)

optional 流: boolean | Record<string, string>

如果你将其设置为 true 或提供一个 Record 值,它将以流式响应的形式呈现。

🌐 If you set it to true or provide a Record value, it will be rendered as a streaming response.

tsx
const AsyncComponent = async () => {
  await new Promise((r) => setTimeout(r, 1000)) // sleep 1s
  return <div>Hi!</div>
}

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

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

如果设置了 true,将添加以下头信息:

🌐 If true is set, the following headers are added:

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

你可以通过指定记录值来自定义标头值。

🌐 You can customize the header values by specifying the Record values.

基于函数的选项

🌐 Function-based Options

你可以传递一个接收 Context 对象的函数,而不是静态的选项对象。这允许你根据请求上下文动态设置选项,例如环境变量或请求参数。

🌐 You can pass a function that receives a Context object instead of a static options object. This allows you to dynamically set options based on the request context, such as environment variables or request parameters.

tsx
app.use(
  '*',
  jsxRenderer(
    ({ children }) => {
      return (
        <html>
          <body>{children}</body>
        </html>
      )
    },
    (c) => ({
      stream: c.req.header('X-Enable-Streaming') === 'true',
    })
  )
)

作为一个具体的例子,你可以在使用 <Suspense> 生成静态站点(SSG)时,使用 isSSGContext 辅助函数来禁用流式传输:

🌐 As a concrete example, you can use this to disable streaming when generating static sites (SSG) with <Suspense>, by using the isSSGContext helper:

tsx
app.use(
  '*',
  jsxRenderer(
    ({ children }) => {
      return (
        <div>
          <Suspense fallback={'loading...'}>
            <Component />
          </Suspense>
        </div>
      )
    },
    (c) => ({
      stream: !isSSGContext(c),
    })
  )
)

嵌套布局

🌐 Nested Layouts

Layout 组件支持嵌套布局。

🌐 The Layout component enables nesting the layouts.

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

const blog = new Hono()
blog.use(
  jsxRenderer(({ children, Layout }) => {
    return (
      <Layout>
        <nav>Blog Menu</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>
      You are accessing: <RequestUrlBadge />
    </div>
  )
})

WARNING

你不能将 useRequestContext() 与 Deno 的 precompile JSX 选项一起使用。请使用 react-jsx

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

扩展 ContextRenderer

🌐 Extending ContextRenderer

通过如下定义 ContextRenderer,你可以向渲染器传递额外的内容。例如,当你想根据页面更改 head 标签的内容时,这非常方便。

🌐 By defining ContextRenderer as shown below, you can pass additional content to the renderer. This is handy, for instance, when you want to change the contents of the head tag depending on the page.

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>Menu</header>
          <div>{children}</div>
        </body>
      </html>
    )
  })
)

app.get('/page/favorites', (c) => {
  return c.render(
    <div>
      <ul>
        <li>Eating sushi</li>
        <li>Watching baseball games</li>
      </ul>
    </div>,
    {
      title: 'My favorites',
    }
  )
})

Hono 中文网 - 粤ICP备13048890号