mirror of
https://github.com/mintlify/docs.git
synced 2026-09-14 13:35:46 +08:00
4b465465dd
Generated-By: mintlify-agent Co-authored-by: mintlify[bot] <109931778+mintlify[bot]@users.noreply.github.com>
258 lines
8.7 KiB
Plaintext
258 lines
8.7 KiB
Plaintext
---
|
||
title: "React"
|
||
description: "使用 JSX、状态和客户端逻辑,在你的 Mintlify 文档中通过自定义 React 组件构建交互且可复用的元素。"
|
||
keywords: ["React 组件", "交互式组件", "JSX", "自定义组件"]
|
||
boost: 3
|
||
---
|
||
|
||
import { ColorGenerator } from "/snippets/color-generator.jsx";
|
||
|
||
[React 组件](https://react.dev) 是在文档中创建交互式、可复用元素的强大方式。
|
||
|
||
<div id="using-react-components">
|
||
## 使用 React 组件
|
||
</div>
|
||
|
||
可以直接在 MDX 文件中使用 [React hooks](https://react.dev/reference/react/hooks) 来构建 React 组件。
|
||
|
||
<div id="example">
|
||
### 示例
|
||
</div>
|
||
|
||
本示例先声明一个 `Counter` 组件,然后通过 `<Counter />` 使用它。
|
||
|
||
```mdx
|
||
export const Counter = () => {
|
||
const [count, setCount] = useState(0)
|
||
|
||
const increment = () => setCount(count + 1)
|
||
const decrement = () => setCount(count - 1)
|
||
|
||
return (
|
||
<div className="flex items-center justify-center">
|
||
<div className="flex items-center rounded-xl overflow-hidden border border-zinc-950/20 dark:border-white/20">
|
||
<button
|
||
onClick={decrement}
|
||
className="flex items-center justify-center h-8 w-8 text-zinc-950/80 dark:text-white/80 border-r border-zinc-950/20 dark:border-white/20"
|
||
aria-label="减少"
|
||
>
|
||
-
|
||
</button>
|
||
|
||
<div className="flex text-sm items-center justify-center h-8 px-6 text-zinc-950/80 dark:text-white/80 font-medium min-w-[4rem] text-center">
|
||
{count}
|
||
</div>
|
||
|
||
<button
|
||
onClick={increment}
|
||
className="flex items-center justify-center h-8 w-8 text-zinc-950/80 dark:text-white/80 border-l border-zinc-950/20 dark:border-white/20"
|
||
aria-label="增加"
|
||
>
|
||
+
|
||
</button>
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|
||
|
||
<Counter />
|
||
```
|
||
|
||
export const Counter = () => {
|
||
const [count, setCount] = useState(0)
|
||
|
||
const increment = () => setCount(count + 1)
|
||
const decrement = () => setCount(count - 1)
|
||
|
||
return (
|
||
<div className="flex items-center justify-center">
|
||
<div className="flex items-center rounded-xl overflow-hidden border border-zinc-950/20 dark:border-white/20">
|
||
<button
|
||
onClick={decrement}
|
||
className="flex items-center justify-center h-8 w-8 text-zinc-950/80 dark:text-white/80 border-r border-zinc-950/20 dark:border-white/20"
|
||
aria-label="减一"
|
||
>
|
||
-
|
||
</button>
|
||
|
||
<div className="flex text-sm items-center justify-center h-8 px-6 text-zinc-950/80 dark:text-white/80 font-medium min-w-[4rem] text-center">
|
||
{count}
|
||
</div>
|
||
|
||
<button
|
||
onClick={increment}
|
||
className="flex items-center justify-center h-8 w-8 text-zinc-950/80 dark:text-white/80 border-l border-zinc-950/20 dark:border-white/20"
|
||
aria-label="加一"
|
||
>
|
||
+
|
||
</button>
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|
||
|
||
计数器以交互式 React 组件的形式进行渲染。
|
||
|
||
<Counter />
|
||
|
||
<div id="importing-components">
|
||
## 导入组件
|
||
</div>
|
||
|
||
要在 MDX 文件中使用 React 组件,组件文件必须放在 `/snippets/` 文件夹中。了解更多关于[可复用片段](/zh/create/reusable-snippets)的内容。
|
||
|
||
<Note>
|
||
不支持嵌套导入。如果某个 React 组件引用了其他组件,你必须在父级 MDX 文件中直接导入所有这些组件,而不是在组件文件内部再导入组件。
|
||
</Note>
|
||
|
||
### 示例
|
||
|
||
此示例定义了一个 `ColorGenerator` 组件,它使用了多个 React Hook,然后在一个 MDX 文件中使用该组件。
|
||
|
||
在 `snippets` 文件夹中创建 `color-generator.jsx` 文件:
|
||
|
||
```mdx /snippets/color-generator.jsx [expandable]
|
||
export const ColorGenerator = () => {
|
||
const [hue, setHue] = useState(180)
|
||
const [saturation, setSaturation] = useState(50)
|
||
const [lightness, setLightness] = useState(50)
|
||
const [colors, setColors] = useState([])
|
||
|
||
useEffect(() => {
|
||
const newColors = []
|
||
for (let i = 0; i < 5; i++) {
|
||
const l = Math.max(10, Math.min(90, lightness - 20 + i * 10))
|
||
newColors.push(`hsl(${hue}, ${saturation}%, ${l}%)`)
|
||
}
|
||
setColors(newColors)
|
||
}, [hue, saturation, lightness])
|
||
|
||
const copyToClipboard = (color) => {
|
||
navigator.clipboard
|
||
.writeText(color)
|
||
.then(() => {
|
||
console.log(`已复制 ${color} 到剪贴板!`)
|
||
})
|
||
.catch((err) => {
|
||
console.error("复制失败:", err)
|
||
})
|
||
}
|
||
|
||
return (
|
||
<div className="p-4 border dark:border-zinc-950/80 rounded-xl not-prose">
|
||
<div className="space-y-4">
|
||
<div className="space-y-2">
|
||
<label className="block text-sm text-zinc-950/70 dark:text-white/70">
|
||
色相:{hue}°
|
||
<input
|
||
type="range"
|
||
min="0"
|
||
max="360"
|
||
value={hue}
|
||
onChange={(e) => setHue(Number.parseInt(e.target.value))}
|
||
className="w-full h-2 bg-zinc-950/20 rounded-lg appearance-none cursor-pointer dark:bg-white/20 mt-1"
|
||
style={{
|
||
background: `linear-gradient(to right,
|
||
hsl(0, ${saturation}%, ${lightness}%),
|
||
hsl(60, ${saturation}%, ${lightness}%),
|
||
hsl(120, ${saturation}%, ${lightness}%),
|
||
hsl(180, ${saturation}%, ${lightness}%),
|
||
hsl(240, ${saturation}%, ${lightness}%),
|
||
hsl(300, ${saturation}%, ${lightness}%),
|
||
hsl(360, ${saturation}%, ${lightness}%))`,
|
||
}}
|
||
/>
|
||
</label>
|
||
|
||
<label className="block text-sm text-zinc-950/70 dark:text-white/70">
|
||
饱和度:{saturation}%
|
||
<input
|
||
type="range"
|
||
min="0"
|
||
max="100"
|
||
value={saturation}
|
||
onChange={(e) => setSaturation(Number.parseInt(e.target.value))}
|
||
className="w-full h-2 bg-zinc-950/20 rounded-lg appearance-none cursor-pointer dark:bg-white/20 mt-1"
|
||
style={{
|
||
background: `linear-gradient(to right,
|
||
hsl(${hue}, 0%, ${lightness}%),
|
||
hsl(${hue}, 50%, ${lightness}%),
|
||
hsl(${hue}, 100%, ${lightness}%))`,
|
||
}}
|
||
/>
|
||
</label>
|
||
|
||
<label className="block text-sm text-zinc-950/70 dark:text-white/70">
|
||
明度:{lightness}%
|
||
<input
|
||
type="range"
|
||
min="0"
|
||
max="100"
|
||
value={lightness}
|
||
onChange={(e) => setLightness(Number.parseInt(e.target.value))}
|
||
className="w-full h-2 bg-zinc-950/20 rounded-lg appearance-none cursor-pointer dark:bg-white/20 mt-1"
|
||
style={{
|
||
background: `linear-gradient(to right,
|
||
hsl(${hue}, ${saturation}%, 0%),
|
||
hsl(${hue}, ${saturation}%, 50%),
|
||
hsl(${hue}, ${saturation}%, 100%))`,
|
||
}}
|
||
/>
|
||
</label>
|
||
</div>
|
||
|
||
<div className="flex space-x-1">
|
||
{colors.map((color, idx) => (
|
||
<div
|
||
key={idx}
|
||
className="h-16 rounded flex-1 cursor-pointer transition-transform hover:scale-105"
|
||
style={{ backgroundColor: color }}
|
||
title={`点击复制:${color}`}
|
||
onClick={() => copyToClipboard(color)}
|
||
/>
|
||
))}
|
||
</div>
|
||
|
||
<div className="text-sm font-mono text-zinc-950/70 dark:text-white/70">
|
||
<p>
|
||
基础颜色:hsl({hue}, {saturation}%, {lightness}%)
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|
||
```
|
||
|
||
导入 `ColorGenerator` 组件,并在一个 MDX 文件中使用:
|
||
|
||
```mdx
|
||
import { ColorGenerator } from "/snippets/color-generator.jsx"
|
||
|
||
<ColorGenerator />
|
||
```
|
||
|
||
颜色生成器会呈现为一个交互式 React 组件。
|
||
|
||
<ColorGenerator />
|
||
|
||
<div id="considerations">
|
||
## 注意事项
|
||
</div>
|
||
|
||
<AccordionGroup>
|
||
<Accordion title="客户端渲染的影响">
|
||
React Hook 组件在客户端渲染,这会带来以下影响:
|
||
|
||
* **SEO (搜索引擎优化) **:搜索引擎可能无法完整索引动态内容。
|
||
* **初始加载**:在组件渲染前,访客可能会看到短暂的加载占位或空白。
|
||
* **无障碍**:确保动态内容的变更能被屏幕阅读器及时播报。
|
||
</Accordion>
|
||
|
||
<Accordion title="性能最佳实践">
|
||
* **优化依赖数组**:在 `useEffect` 的依赖数组中仅包含必要的依赖。
|
||
* **记忆化复杂计算**:对开销较大的操作使用 `useMemo` 或 `useCallback`。
|
||
* **减少重复渲染**:将大型组件拆分为更小组件,避免级联式重复渲染。
|
||
* **懒加载**:考虑对复杂组件进行懒加载,以改善页面的首屏加载时间。
|
||
</Accordion>
|
||
</AccordionGroup> |