本页还没有译文
本页 42 段还没译成简体中文,下面显示的是英文原文。JSX
还没翻译译自英文(hono.dev)本页 42 段都还没译
模型写的
背景主张方法设置结果局限
JSX
通过 hono/jsx 用 JSX 语法编写 HTML
You can write HTML with JSX syntax with hono/jsx.You can write HTML with JSX syntax with hono/jsx.通过 hono/jsx 用 JSX 语法编写 HTML 这是本页服务端与客户端渲染的前提通过 hono/jsx 用 JSX 语法编写 HTMLJSX 语法
Although hono/jsx works on the client, you will probably use it most often when rendering content on the server side. Here are some things related to JSX that are common to both server and client.Although hono/jsx works on the client, you will probably use it most often when rendering content on the server side. Here are some things related to JSX that are common to both server and client.hono/jsx 可用于客户端,但多用于服务端 所述的共享 JSX 特性在两个环境都适用hono/jsx 可用于客户端,但多用于服务端服务端客户端
Settings
为 hono/jsx 配置 tsconfig 或 deno.json
To use JSX, modify the tsconfig.json:To use JSX, modify the tsconfig.json:修改 tsconfig.json 以使用 JSX 它设置 JSX 支持所需的编译器选项修改 tsconfig.json 以使用 JSXtsconfig.json
tsconfig.json:
{
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "hono/jsx"
}
}Alternatively, use the pragma directives:Alternatively, use the pragma directives:pragma 指令可替代 tsconfig 设置 它们按文件内联声明 jsx 与 jsxImportSourcepragma 指令可替代 tsconfig 设置pragma 指令
/** @jsx jsx */
/** @jsxImportSource hono/jsx */For Deno, you have to modify the deno.json instead of the tsconfig.json:For Deno, you have to modify the deno.json instead of the tsconfig.json:Deno 用户改 deno.json 而非 tsconfig.json 它使用 precompile 模式和 @hono/hono/jsxDeno 用户改 deno.json 而非 tsconfig.jsondeno.json
{
"compilerOptions": {
"jsx": "precompile",
"jsxImportSource": "@hono/hono/jsx"
}
}Usage
入口改为 .tsx 并用 c.html 渲染组件
Note
If you are coming straight from the Quick Start, the main file has a .ts extension - you need to change it to .tsx - otherwise you will not be able to run the application at all. You should additionally modify the package.json (or deno.json if you are using Deno) to reflect that change (e.g. instead of having bun run --hot src/index.ts in dev script, you should have bun run --hot src/index.tsx).You should additionally modify the package.json (or deno.json if you are using Deno) to reflect that change (e.g. instead of having bun run --hot src/index.ts in dev script, you should have bun run --hot src/index.tsx).主文件须改为 .tsx,否则应用无法运行 需同步更新 package.json 或 deno.json 的 dev 脚本主文件须改为 .tsx,否则应用无法运行主文件
index.tsx:
import { Hono } from 'hono'
import type { FC } from 'hono/jsx'
const app = new Hono()
const Layout: FC = (props) => {
return (
<html>
<body>{props.children}</body>
</html>
)
}
const Top: FC<{ messages: string[] }> = (props: {
messages: string[]
}) => {
return (
<Layout>
<h1>Hello Hono!</h1>
<ul>
{props.messages.map((message) => {
return <li>{message}!!</li>
})}
</ul>
</Layout>
)
}
app.get('/', (c) => {
const messages = ['Good Morning', 'Good Evening', 'Good Night']
return c.html(<Top messages={messages} />)
})
export default appMetadata hoisting
title、link 和 meta 标签自动提升到 head
You can write document metadata tags such as <title>, <link>, and <meta> directly inside your components. These tags will be automatically hoisted to the <head> section of the document. This is especially useful when the <head> element is rendered far from the component that determines the appropriate metadata.You can write document metadata tags such as <title>, <link>, and <meta> directly inside your components. These tags will be automatically hoisted to the <head> section of the document. This is especially useful when the <head> element is rendered far from the component that determines the appropriate metadata.title、link 和 meta 自动提升到 head 当 head 远离元数据组件时尤其有用title、link 和 meta 自动提升到 headtitle link meta
import { Hono } from 'hono'
const app = new Hono()
app.use('*', async (c, next) => {
c.setRenderer((content) => {
return c.html(
<html>
<head></head>
<body>{content}</body>
</html>
)
})
await next()
})
app.get('/about', (c) => {
return c.render(
<>
<title>About Page</title>
<meta name='description' content='This is the about page.' />
about page content
</>
)
})
export default appNote
When hoisting occurs, existing elements are not removed. Elements appearing later are added to the end. For example, if you have <title>Default</title> in your <head> and a component renders <title>Page Title</title>, both titles will appear in the head.When hoisting occurs, existing elements are not removed. Elements appearing later are added to the end. For example, if you have <title>Default</title> in your <head> and a component renders <title>Page Title</title>, both titles will appear in the head.提升不删除已有元素,后出现的追加在后 因此默认标题与页面标题都出现在 head 中提升不删除已有元素,后出现的追加在后提升元素
Fragment
用 Fragment 或 <> 分组元素而不加额外节点
Use Fragment to group multiple elements without adding extra nodes:Use Fragment to group multiple elements without adding extra nodes:Fragment 分组多个元素且不加额外节点 从 hono/jsx 导入它来包裹子元素Fragment 分组多个元素且不加额外节点Fragment
import { Fragment } from 'hono/jsx'
const List = () => (
<Fragment>
<p>first child</p>
<p>second child</p>
<p>third child</p>
</Fragment>
)Or you can write it with <></> if it sets up properly.Or you can write it with <></> if it sets up properly.配置正确时可用 <> 简写代替 Fragment 它避免显式导入 Fragment配置正确时可用 <> 简写代替 Fragment简写 Fragment
const List = () => (
<>
<p>first child</p>
<p>second child</p>
<p>third child</p>
</>
)PropsWithChildren
PropsWithChildren 在函数组件中推断子元素
You can use PropsWithChildren to correctly infer a child element in a function component.You can use PropsWithChildren to correctly infer a child element in a function component.PropsWithChildren 在函数组件中推断子元素 包裹 props 类型即可正确类型化 childrenPropsWithChildren 在函数组件中推断子元素PropsWithChildren
import { PropsWithChildren } from 'hono/jsx'
type Post = {
id: number
title: string
}
function Component({ title, children }: PropsWithChildren<Post>) {
return (
<div>
<h1>{title}</h1>
{children}
</div>
)
}Inserting Raw HTML
dangerouslySetInnerHTML 直接插入原始 HTML
To directly insert HTML, use dangerouslySetInnerHTML:To directly insert HTML, use dangerouslySetInnerHTML:dangerouslySetInnerHTML 直接插入原始 HTML 传入含 __html 标记的对象dangerouslySetInnerHTML 直接插入原始 HTMLdangerouslySetInnerHTML
app.get('/foo', (c) => {
const inner = { __html: 'JSX · SSR' }
const Div = <div dangerouslySetInnerHTML={inner} />
})Memoization
memo 通过缓存计算字符串优化组件
Optimize your components by memoizing computed strings using memo:Optimize your components by memoizing computed strings using memo:memo 通过缓存计算字符串优化组件 用 hono/jsx 导入的 memo 包裹组件memo 通过缓存计算字符串优化组件memo
import { memo } from 'hono/jsx'
const Header = memo(() => <header>Welcome to Hono</header>)
const Footer = memo(() => <footer>Powered by Hono</footer>)
const Layout = (
<div>
<Header />
<p>Hono is cool!</p>
<Footer />
</div>
)Context
useContext 在组件树中共享数据
By using useContext, you can share data globally across any level of the Component tree without passing values through props.By using useContext, you can share data globally across any level of the Component tree without passing values through props.useContext 在组件树中共享数据 无需逐层通过 props 传递值useContext 在组件树中共享数据useContext
import type { FC } from 'hono/jsx'
import { createContext, useContext } from 'hono/jsx'
const themes = {
light: {
color: '#000000',
background: '#eeeeee',
},
dark: {
color: '#ffffff',
background: '#222222',
},
}
const ThemeContext = createContext(themes.light)
const Button: FC = () => {
const theme = useContext(ThemeContext)
return <button style={theme}>Push!</button>
}
const Toolbar: FC = () => {
return (
<div>
<Button />
</div>
)
}
// ...
app.get('/', (c) => {
return c.html(
<div>
<ThemeContext.Provider value={themes.dark}>
<Toolbar />
</ThemeContext.Provider>
</div>
)
})Async Component
异步组件由 c.html 自动等待
hono/jsx supports an Async Component, so you can use async/await in your component.
If you render it with c.html(), it will await automatically.hono/jsx supports an Async Component, so you can use async/await in your component. If you render it with c.html(), it will await automatically.异步组件用 await,c.html 会自动等待 这使得组件内可进行异步数据获取异步组件用 await,c.html 会自动等待异步组件
const AsyncComponent = async () => {
await new Promise((r) => setTimeout(r, 1000)) // sleep 1s
return <div>Done!</div>
}
app.get('/', (c) => {
return c.html(
<html>
<body>
<AsyncComponent />
</body>
</html>
)
})Suspense Experimental
Suspense 先渲染 fallback 再显示等待内容
The React-like Suspense feature is available.
If you wrap the async component with Suspense, the content in the fallback will be rendered first, and once the Promise is resolved, the awaited content will be displayed.
You can use it with renderToReadableStream().The React-like Suspense feature is available. If you wrap the async component with Suspense, the content in the fallback will be rendered first, and once the Promise is resolved, the awaited content will be displayed. You can use it with renderToReadableStream().Suspense 先渲染 fallback 再显示等待内容 配合 renderToReadableStream 实现流式输出Suspense 先渲染 fallback 再显示等待内容Suspense
import { renderToReadableStream, Suspense } from 'hono/jsx/streaming'
//...
app.get('/', (c) => {
const stream = renderToReadableStream(
<html>
<body>
<Suspense fallback={<div>loading...</div>}>
<Component />
</Suspense>
</body>
</html>
)
return c.body(stream, {
headers: {
'Content-Type': 'text/html; charset=UTF-8',
'Transfer-Encoding': 'chunked',
},
})
})ErrorBoundary Experimental
ErrorBoundary 捕获子组件错误并显示 fallback
You can catch errors in child components using ErrorBoundary.You can catch errors in child components using ErrorBoundary.ErrorBoundary 捕获子组件中的错误 包裹组件以拦截抛出的错误ErrorBoundary 捕获子组件中的错误ErrorBoundary
In the example below, it will show the content specified in fallback if an error occurs.In the example below, it will show the content specified in fallback if an error occurs.发生错误时显示 fallback 内容 抛出的 Error 触发边界的 fallback 渲染发生错误时显示 fallback 内容fallback 内容
function SyncComponent() {
throw new Error('Error')
return <div>Hello</div>
}
app.get('/sync', async (c) => {
return c.html(
<html>
<body>
<ErrorBoundary fallback={<div>Out of Service</div>}>
<SyncComponent />
</ErrorBoundary>
</body>
</html>
)
})ErrorBoundary can also be used with async components and Suspense.ErrorBoundary can also be used with async components and Suspense.ErrorBoundary 也可用于异步组件和 Suspense 它包裹 Suspense 以捕获异步失败ErrorBoundary 也可用于异步组件和 SuspenseErrorBoundary
async function AsyncComponent() {
await new Promise((resolve) => setTimeout(resolve, 2000))
throw new Error('Error')
return <div>Hello</div>
}
app.get('/with-suspense', async (c) => {
return c.html(
<html>
<body>
<ErrorBoundary fallback={<div>Out of Service</div>}>
<Suspense fallback={<div>Loading...</div>}>
<AsyncComponent />
</Suspense>
</ErrorBoundary>
</body>
</html>
)
})StreamingContext Experimental
StreamingContext 为生成的 script 标签添加 scriptNonce
You can use StreamingContext to provide configuration for streaming components like Suspense and ErrorBoundary. This is useful for adding nonce values to script tags generated by these components for Content Security Policy (CSP).You can use StreamingContext to provide configuration for streaming components like Suspense and ErrorBoundary. This is useful for adding nonce values to script tags generated by these components for Content Security Policy (CSP).StreamingContext 为流式组件配置 scriptNonce 以支持 CSP 它为 Suspense 和 ErrorBoundary 生成的脚本添加 nonceStreamingContext 为流式组件配置 scriptNonce 以支持 CSPStreamingContext
import { Suspense, StreamingContext } from 'hono/jsx/streaming'
// ...
app.get('/', (c) => {
const stream = renderToReadableStream(
<html>
<body>
<StreamingContext
value={{ scriptNonce: 'random-nonce-value' }}
>
<Suspense fallback={<div>Loading...</div>}>
<AsyncComponent />
</Suspense>
</StreamingContext>
</body>
</html>
)
return c.body(stream, {
headers: {
'Content-Type': 'text/html; charset=UTF-8',
'Transfer-Encoding': 'chunked',
'Content-Security-Policy':
"script-src 'nonce-random-nonce-value'",
},
})
})The scriptNonce value will be automatically added to any <script> tags generated by Suspense and ErrorBoundary components.The scriptNonce value will be automatically added to any <script> tags generated by Suspense and ErrorBoundary components.scriptNonce 自动添加到生成的 script 标签 Suspense 与 ErrorBoundary 的脚本继承配置的 noncescriptNonce 自动添加到生成的 script 标签scriptNonce
Integration with html Middleware
组合 JSX 与 html 中间件实现强大模板
Combine the JSX and HTML middlewares for powerful templating. For in-depth details, consult the HTML middleware documentation.Combine the JSX and HTML middlewares for powerful templating. For in-depth details, consult the HTML middleware documentation.组合 JSX 与 HTML 中间件实现强大模板 深入细节参见 HTML 中间件文档组合 JSX 与 HTML 中间件实现强大模板HTML 中间件
import { Hono } from 'hono'
import { html } from 'hono/html'
const app = new Hono()
interface SiteData {
title: string
children?: any
}
const Layout = (props: SiteData) =>
html`<!doctype html>
<html>
<head>
<title>${props.title}</title>
</head>
<body>
${props.children}
</body>
</html>`
const Content = (props: { siteData: SiteData; name: string }) => (
<Layout {...props.siteData}>
<h1>Hello {props.name}</h1>
</Layout>
)
app.get('/:name', (c) => {
const { name } = c.req.param()
const props = {
name: name,
siteData: {
title: 'JSX with html sample',
},
}
return c.html(<Content {...props} />)
})
export default appWith JSX Renderer Middleware
JSX Renderer Middleware 让创建 HTML 页面更简单
The JSX Renderer Middleware allows you to create HTML pages more easily with the JSX.The JSX Renderer Middleware allows you to create HTML pages more easily with the JSX.JSX Renderer Middleware 让创建 HTML 页面更简单 它将 JSX 渲染封装为可复用中间件JSX Renderer Middleware 让创建 HTML 页面更简单JSX Renderer Middleware
Override type definitions
覆盖类型定义以添加自定义元素和属性
You can override the type definition to add your custom elements and attributes.You can override the type definition to add your custom elements and attributes.覆盖类型定义以添加自定义元素和属性 声明 hono/jsx 模块并扩展 JSX IntrinsicElements覆盖类型定义以添加自定义元素和属性类型定义
declare module 'hono/jsx' {
namespace JSX {
interface IntrinsicElements {
'my-custom-element': HTMLAttributes & {
'x-event'?: 'click' | 'scroll'
}
}
}
}