作为一名前端学习者,React 的组件化思想一直是我日常学习的核心,而 props 作为组件之间通信的核心桥梁,更是贯穿了从基础组件到高级封装的全过程。最近通过实现 Greeting、Modal、Card 三个组件,对 props 的使用有了更细致的感悟,今天就来和大家分享我的实战心得。
在前端开发(尤其是 React)中,props 是用来从父组件向子组件传递数据的一种机制。
简单理解:
- props 就像函数的参数:调用一个函数时传入参数,组件被使用时也可以传入 props。
- props 是只读的:子组件不能修改接收到的 props,只能读取和使用。
- props 可以是任何类型:字符串、数字、对象、数组、函数,甚至其他 React 元素。
一、Greeting 组件
Greeting 组件用是来展示/实现props 基础使用、类型校验与默认值
1. props 基础接收与解构
props 是以参数的形式传入的,本质是个对象。用对象解构直接获取需要的属性:
javascript
function Greeting(props) {
// 解构获取 props 中的属性
const {name, message, showIcon} = props;
return (
<div>
{/* 条件渲染:当 showIcon 为 true 时显示图标 */}
{showIcon && <span>👋</span>}
<h1>Hello, {name} </h1>
<p>{message}</p>
</div>
)
}
在使用组件时,我们只需像传递 HTML 属性一样传递 props 即可:
javascript
// 传递完整属性
<Greeting name="张三" message="你好" showIcon />
// 只传递必传属性
<Greeting name="四" />
有个小细节:
showIcon是一个布尔类型的 props,当我们只写属性名不赋值时,它的默认值就是true,这是 React 的语法糖,非常便捷。
2. props 类型校验:避免传参错误
在团队开发中,经常会出现 props 类型不匹配的问题(比如把数字当成字符串传递),这时 prop-types 库就能帮我们做校验。
给 Greeting 组件做了明确的类型约定,指定了属性的类型和是否必传:
javascript
import PropTypes from 'prop-types';
Greeting.propTypes = {
name: PropTypes.string.isRequired, // 必传字符串,不传会报错
message: PropTypes.string, // 可选字符串,非必传
showIcon: PropTypes.bool, // 可选布尔值,非必传
};
其中 isRequired 关键字表示该属性是必传项,如果父组件没有传递,控制台会抛出警告信息,帮我们定位问题。
3. defaultProps:设置 props 默认值
有些 props 不是必传的,但我们希望它有一个默认值,这时候 defaultProps 就派上用场了。比如 Greeting 组件的 message 属性,我给它设置了默认的问候语:
javascript
Greeting.defaultProps = {
message: '圣诞节快乐',
};
当父组件没有传递 message 属性时,组件会自动使用这个默认值,就像我只传递 name="磊" 时,页面会显示默认的 "圣诞节快乐",大大提升了组件的灵活性。

二、Modal 组件
Modal 弹窗组件就是 props 高级用法的体现 ------ 它通过 props 传递自定义组件,实现了弹窗头部、底部的灵活定制,让组件的复用性大大提升。
1. 接收组件类型的 props
Modal 组件的核心需求是:头部、底部可自定义,中间区域支持插入任意内容。我通过 props 接收头部组件(HeaderComponent)和底部组件(FooterComponent),再在组件内部渲染它们:
javascript
function Modal(props) {
// 解构获取子组件和自定义头部、底部组件
const {children, HeaderComponent, FooterComponent} = props;
return (
<div style={styles.overlay}>
<div style={styles.modal}>
{/* 渲染自定义头部组件 */}
<HeaderComponent />
<div style={styles.content}></div>
{/* 渲染中间自定义内容(children) */}
{children}
{/* 渲染自定义底部组件 */}
<FooterComponent />
</div>
</div>
)
}
这里的 HeaderComponent 和 FooterComponent 本质上是函数(React 组件本质是函数 / 类),通过 props 传递后,我们可以像使用普通组件一样直接渲染。
2. children props:组件的 "插槽"
在 Modal 组件中,children 是一个特殊的 props,它对应着组件标签之间包裹的所有内容,相当于 Vue 中的插槽。通过 children,我们可以给 Modal 插入任意 JSX 内容,实现中间区域的定制化:
javascript
<Modal
HeaderComponent={MyHeader}
FooterComponent={MyFooter}
>
{/* 这里的内容会被 children 接收并渲染 */}
<p>这是一个弹窗</p>
<p>你可以在这里显示任何JSX</p>
</Modal>
我们可以在这里打印一下props

而自定义的头部和底部组件,只需在父组件中定义好,再通过 props 传递给 Modal 即可:
javascript
// 自定义弹窗头部
const MyHeader = () => {
return (
<h2 style={{margin:0,color:'red'}}>自定义标题</h2>
)
}
// 自定义弹窗底部
const MyFooter = () => {
return (
<div style={{textAlign:'right'}}>
<button
onClick={() => alert('取消')}
style={{padding:'0.5rem 1rem'}}
>取消</button>
</div>
)
}

这种方式让 Modal 组件不再是固定结构,而是可以根据业务需求灵活定制,大大提升了组件的复用性和扩展性。
三、Card 组件:props 结合 className,实现样式灵活扩展
Card 卡片组件是日常开发中使用频率极高的组件,我在实现它时,通过 props 接收自定义 className,实现了基础样式与自定义样式的结合,让卡片样式既统一又灵活。
1. 基础样式封装
首先,在 Card.css 中定义了卡片的基础样式,包括背景、圆角、阴影、内边距等,保证所有 Card 组件有统一的视觉风格:
css
.card {
background-color: #ffffff;
border-radius: 12px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
padding: 20px;
margin: 16px auto;
max-width: 400px;
transition: all 0.3s ease;
overflow: hidden;
}
.card:hover {
transform: translateY(-4px);
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.15);
}
2. props 接收 className,扩展自定义样式
为了让 Card 组件支持自定义样式,我在组件中接收 className 属性,并给它设置一个空字符串作为默认值,然后通过模板字符串将基础 className(card)和自定义 className 拼接起来:
javascript
const Card = ({ children, className = '' }) => {
return (
<div className={`card ${className}`}>
{children}
</div>
);
};
这样一来,当我们需要给某个卡片添加特殊样式时,只需传递自定义 className 即可:
javascript
<Card className="user-card">
<h2>张三</h2>
<p>高级前端工程师</p>
<button>查看详情</button>
</Card>

基础样式 card 保证了卡片的统一风格,自定义样式 user-card 可以满足个性化需求,这种方式既避免了样式冗余,又提升了组件的灵活性。同时,我也使用了 children props,让卡片内部可以插入任意内容,进一步提升了组件的复用性。
总结
通过实现 Greeting、Modal、Card 这三个组件,我对 React props 有了更全面的理解:
- 基础层面 (Greeting):props 是组件间传递数据的核心,配合
prop-types类型校验和defaultProps默认值,能让组件更健壮、更易用; - 高级层面 (Modal):props 不仅可以传递基本类型数据,还可以传递组件,结合
children插槽,能实现高度定制化的组件封装; - 样式层面(Card):props 接收 className,能实现基础样式与自定义样式的灵活结合,兼顾样式统一性和个性化。