Skip to content

客户端组件

¥Client Components

hono/jsx 不仅支持服务器端,还支持客户端。这意味着可以创建一个在浏览器中运行的交互式 UI。我们称之为客户端组件或 hono/jsx/dom

¥hono/jsx supports not only server side but also client side. This means that it is possible to create an interactive UI that runs in the browser. We call it Client Components or hono/jsx/dom.

它速度快而且非常小。hono/jsx/dom 中的计数器程序使用 Brotli 压缩后只有 2.8KB。但是,React 占用 47.8KB。

¥It is fast and very small. The counter program in hono/jsx/dom is only 2.8KB with Brotli compression. But, 47.8KB for React.

本节介绍客户端组件特有的功能。

¥This section introduces Client Components-specific features.

反例

¥Counter example

以下是简单计数器的示例,相同的代码与 React 中的工作方式相同。

¥Here is an example of a simple counter, the same code works as in React.

tsx
import { useState } from 'hono/jsx'
import { render } from 'hono/jsx/dom'

function Counter() {
  const [count, setCount] = useState(0)
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  )
}

function App() {
  return (
    <html>
      <body>
        <Counter />
      </body>
    </html>
  )
}

const root = document.getElementById('root')
render(<App />, root)

render()

你可以使用 render() 在指定的 HTML 元素内插入 JSX 组件。

¥You can use render() to insert JSX components within a specified HTML element.

tsx
render(<Component />, container)

与 React 兼容的 Hooks

¥Hooks compatible with React

hono/jsx/dom 具有与 React 兼容或部分兼容的 Hooks。你可以通过查看 React 文档 来了解这些 API。

¥hono/jsx/dom has Hooks that are compatible or partially compatible with React. You can learn about these APIs by looking at the React documentation.

  • useState()

  • useEffect()

  • useRef()

  • useCallback()

  • use()

  • startTransition()

  • useTransition()

  • useDeferredValue()

  • useMemo()

  • useLayoutEffect()

  • useReducer()

  • useDebugValue()

  • createElement()

  • memo()

  • isValidElement()

  • useId()

  • createRef()

  • forwardRef()

  • useImperativeHandle()

  • useSyncExternalStore()

  • useInsertionEffect()

  • useFormStatus()

  • useActionState()

  • useOptimistic()

startViewTransition() 系列

¥startViewTransition() family

startViewTransition() 系列包含原始钩子和函数,可轻松处理 查看转换 API。以下是如何使用它们的示例。

¥The startViewTransition() family contains original hooks and functions to handle View Transitions API easily. The followings are examples of how to use them.

1. 最简单的例子

¥ An easiest example

你可以使用 document.startViewTransitionstartViewTransition() 语法编写过渡。

¥You can write a transition using the document.startViewTransition shortly with the startViewTransition().

tsx
import { useState, startViewTransition } from 'hono/jsx'
import { css, Style } from 'hono/css'

export default function App() {
  const [showLargeImage, setShowLargeImage] = useState(false)
  return (
    <>
      <Style />
      <button
        onClick={() =>
          startViewTransition(() =>
            setShowLargeImage((state) => !state)
          )
        }
      >
        Click!
      </button>
      <div>
        {!showLargeImage ? (
          <img src='https://hono.nodejs.cn/images/logo.png' />
        ) : (
          <div
            class={css`
              background: url('https://hono.nodejs.cn/images/logo-large.png');
              background-size: contain;
              background-repeat: no-repeat;
              background-position: center;
              width: 600px;
              height: 600px;
            `}
          ></div>
        )}
      </div>
    </>
  )
}

2. 将 viewTransition()keyframes() 一起使用

¥ Using viewTransition() with keyframes()

viewTransition() 函数允许你获取唯一的 view-transition-name

¥The viewTransition() function allows you to get the unique view-transition-name.

你可以将它与 keyframes() 一起使用,::view-transition-old() 转换为 ::view-transition-old(${uniqueName))

¥You can use it with the keyframes(), The ::view-transition-old() is converted to ::view-transition-old(${uniqueName)).

tsx
import { useState, startViewTransition } from 'hono/jsx'
import { viewTransition } from 'hono/jsx/dom/css'
import { css, keyframes, Style } from 'hono/css'

const rotate = keyframes`
  from {
    rotate: 0deg;
  }
  to {
    rotate: 360deg;
  }
`

export default function App() {
  const [showLargeImage, setShowLargeImage] = useState(false)
  const [transitionNameClass] = useState(() =>
    viewTransition(css`
      ::view-transition-old() {
        animation-name: ${rotate};
      }
      ::view-transition-new() {
        animation-name: ${rotate};
      }
    `)
  )
  return (
    <>
      <Style />
      <button
        onClick={() =>
          startViewTransition(() =>
            setShowLargeImage((state) => !state)
          )
        }
      >
        Click!
      </button>
      <div>
        {!showLargeImage ? (
          <img src='https://hono.nodejs.cn/images/logo.png' />
        ) : (
          <div
            class={css`
              ${transitionNameClass}
              background: url('https://hono.nodejs.cn/images/logo-large.png');
              background-size: contain;
              background-repeat: no-repeat;
              background-position: center;
              width: 600px;
              height: 600px;
            `}
          ></div>
        )}
      </div>
    </>
  )
}

3. 使用 useViewTransition

¥ Using useViewTransition

如果只想在动画期间更改样式。你可以使用 useViewTransition()。此钩子返回 [boolean, (callback: () => void) => void],它们是 isUpdating 标志和 startViewTransition() 函数。

¥If you want to change the style only during the animation. You can use useViewTransition(). This hook returns the [boolean, (callback: () => void) => void], and they are the isUpdating flag and the startViewTransition() function.

使用此钩子时,组件将在以下两次进行评估。

¥When this hook is used, the Component is evaluated at the following two times.

tsx
import { useState, useViewTransition } from 'hono/jsx'
import { viewTransition } from 'hono/jsx/dom/css'
import { css, keyframes, Style } from 'hono/css'

const rotate = keyframes`
  from {
    rotate: 0deg;
  }
  to {
    rotate: 360deg;
  }
`

export default function App() {
  const [isUpdating, startViewTransition] = useViewTransition()
  const [showLargeImage, setShowLargeImage] = useState(false)
  const [transitionNameClass] = useState(() =>
    viewTransition(css`
      ::view-transition-old() {
        animation-name: ${rotate};
      }
      ::view-transition-new() {
        animation-name: ${rotate};
      }
    `)
  )
  return (
    <>
      <Style />
      <button
        onClick={() =>
          startViewTransition(() =>
            setShowLargeImage((state) => !state)
          )
        }
      >
        Click!
      </button>
      <div>
        {!showLargeImage ? (
          <img src='https://hono.nodejs.cn/images/logo.png' />
        ) : (
          <div
            class={css`
              ${transitionNameClass}
              background: url('https://hono.nodejs.cn/images/logo-large.png');
              background-size: contain;
              background-repeat: no-repeat;
              background-position: center;
              width: 600px;
              height: 600px;
              position: relative;
              ${isUpdating &&
              css`
                &:before {
                  content: 'Loading...';
                  position: absolute;
                  top: 50%;
                  left: 50%;
                }
              `}
            `}
          ></div>
        )}
      </div>
    </>
  )
}

hono/jsx/dom 运行时

¥The hono/jsx/dom runtime

有一个用于客户端组件的小型 JSX 运行时。使用它将产生比使用 ​​hono/jsx 更小的打包结果。在 tsconfig.json 中指定 hono/jsx/dom。对于 Deno,修改 deno.json。

¥There is a small JSX Runtime for Client Components. Using this will result in smaller bundled results than using hono/jsx. Specify hono/jsx/dom in tsconfig.json. For Deno, modify the deno.json.

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

或者,你可以在 vite.config.ts 中的 esbuild 转换选项中指定 hono/jsx/dom

¥Alternatively, you can specify hono/jsx/dom in the esbuild transform options in vite.config.ts.

ts
import { defineConfig } from 'vite'

export default defineConfig({
  esbuild: {
    jsxImportSource: 'hono/jsx/dom',
  },
})

Hono v4.9 中文网 - 粤ICP备13048890号