props
除了可以传递数据,还可以传递组件。
在 props
中,有一个特殊的字段 children
,表示组件嵌套关系中的子元素。请注意,这里我的用词,嵌套关系中的子元素,我们不能笼统的认为 children
就是组件的子元素,这样很容易在后续更高阶的学习中,产生误解。
例如,我们有一个 Button
组件,它的写法如下
1<Button>2<span>Click me</span>3</Button>
此时,我们就可以称 span
为 Button
组件的子元素。再次强调,这里表达的是一种结构上的嵌套关系。
有了这个基础知识之后,我们就可以通过 children
来简化复杂组件的写法。我们可以单独将子组件抽离出去,通过 children
传进来,从而实现组件的复用与逻辑结构的简化。
能够接收 children
的组件,我们称之为容器组件。
例如,我们有一个 Card
组件,它的写法如下,这种情况下,Card
组件就是一个容器组件。容器组件通常与视觉上的理解是一致的,通常表达一种包含关系。
1<Card>2<div>Card Content</div>3</Card>
在日常工作中,容器组件并没有特别明确的定义,因此很多时候就是一种约定俗成的称呼,表达一种可以包含别的组件的组件。
例如我们在整个页面中,封装一个 PageLayout
组件
1<PageLayout>2<div>Page Layout Content</div>3</PageLayout>
或者封装一个弹窗提示组件 Modal
1<Modal>2<div>Modal Content</div>3</Modal>
这里,我们以 Card 组件的封装为例,来详细给大家介绍 children
的运用。
先看一下完整的代码,然后我们再逐步分析。
10import { PropsWithChildren } from 'react'2030interface CardProps extends PropsWithChildren {40title: string50}6070export default function Card(props: CardProps) {80const { children, title } = props9010return (11<div className='border dark:border-0 border-gray-200 dark:inset-ring dark:inset-ring-white/10 rounded-xl'>12<header className='border-b border-gray-200 dark:border-white/10 p-4 font-bold'>13{title}14</header>15<div className='p-2'>16{children}17</div>18</div>19)20}21
children 包含在 props 中,因此在使用时,直接通过 props.children
来获取即可。
在使用之前,我们需要定义一个包含了 children
的 props
类型,这里我们使用 PropsWithChildren
类型。
1import { PropsWithChildren } from 'react'23interface CardProps extends PropsWithChildren {4title: string5}
然后在组件中,我们就可以通过 props.children
来获取子元素即可。
1export default function Card(props: CardProps) {2const { children, title } = props3...4}
然后再补充一点样式,新增一些自定义的属性传入,例如 title
等。就可以正常使用了。例如
10import Card from './card'2030export default function App() {40const darkURL = 'https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*LT2jR41Uj2EAAAAAAAAAAAAADrJ8AQ/original'50const lightURL = 'https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*MLt3R6m9huoAAAAAAAAAAAAADrJ8AQ/original'6070const themeMediaQuery = window.matchMedia('(prefers-color-scheme: light)')8090const url = themeMediaQuery.matches ? lightURL : darkURL1011return (12<div className='flex items-center gap-4'>13<Card title='Typography 排版'>14<div className='w-56 p-4'>15<img src={url} alt='card' className='m-auto w-44' />16</div>17</Card>18</div>19)20}
children
在日常工作中的运用比较简单,但是他是讲复杂结构进行拆分的重要手段。因此在封装自定义组件时会被大量使用。