Props属性 #
一、Props 基础 #
1.1 什么是 Props #
Props(Properties)是组件的输入属性,用于父组件向子组件传递数据。
jsx
// 定义接收 props 的组件
function Greeting(props) {
return <h1>Hello, {props.name}!</h1>;
}
// 传递 props
<Greeting name="World" />
1.2 Props 特点 #
text
┌─────────────────────────────────────────────────────────────┐
│ Props 特点 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. 只读性 │
│ └── 子组件不应修改 props │
│ │
│ 2. 响应式 │
│ └── 传递 Signal 时保持响应性 │
│ │
│ 3. 任意类型 │
│ └── 可以传递任何 JavaScript 值 │
│ │
│ 4. 默认值 │
│ └── 可以为 props 设置默认值 │
│ │
│ 5. 解构 │
│ └── 可以解构 props 对象 │
│ │
└─────────────────────────────────────────────────────────────┘
二、传递 Props #
2.1 基本传递 #
jsx
function Button(props) {
return (
<button
type={props.type}
disabled={props.disabled}
onClick={props.onClick}
>
{props.label}
</button>
);
}
<Button
type="submit"
label="Click me"
disabled={false}
onClick={() => console.log('clicked')}
/>
2.2 解构 Props #
jsx
function Button({ type = 'button', label, disabled = false, onClick }) {
return (
<button type={type} disabled={disabled} onClick={onClick}>
{label}
</button>
);
}
// 注意:解构会失去响应性
// 如果传递的是 Signal,需要特殊处理
2.3 展开传递 #
jsx
function Input(props) {
return <input {...props} />;
}
<Input
type="text"
placeholder="Enter text"
value={text()}
onInput={handleInput}
/>
2.4 传递 Signal #
jsx
function Parent() {
const [count, setCount] = createSignal(0);
return (
<Child
count={count} // 传递 Signal 本身
setCount={setCount}
/>
);
}
function Child(props) {
// 方式1:调用 Signal
return <p>Count: {props.count()}</p>;
// 方式2:传递 Signal 值(失去响应性)
// return <p>Count: {props.count}</p>; // 不会更新
}
三、children 属性 #
3.1 基本用法 #
jsx
function Card(props) {
return (
<div class="card">
{props.children}
</div>
);
}
<Card>
<h2>Title</h2>
<p>Content</p>
</Card>
3.2 children 是函数 #
jsx
function List(props) {
return (
<ul>
{props.children}
</ul>
);
}
<List>
<li>Item 1</li>
<li>Item 2</li>
</List>
3.3 children 类型检查 #
jsx
function Wrapper(props) {
// children 可能是:
// - 单个元素
// - 数组
// - 函数
// - undefined
const children = props.children;
if (typeof children === 'function') {
return children();
}
if (Array.isArray(children)) {
return <div>{children}</div>;
}
return children || null;
}
四、默认值 #
4.1 解构默认值 #
jsx
function Button({
type = 'button',
variant = 'primary',
size = 'medium',
disabled = false,
children
}) {
return (
<button
type={type}
class={`btn btn-${variant} btn-${size}`}
disabled={disabled}
>
{children}
</button>
);
}
<Button>Default Button</Button>
<Button variant="danger" size="large">Delete</Button>
4.2 函数默认值 #
jsx
function Input(props) {
const type = props.type ?? 'text';
const placeholder = props.placeholder ?? 'Enter value';
const onChange = props.onChange ?? (() => {});
return (
<input
type={type}
placeholder={placeholder}
onChange={onChange}
/>
);
}
4.3 使用 mergeProps #
jsx
import { mergeProps } from 'solid-js';
function Button(props) {
const merged = mergeProps({
type: 'button',
variant: 'primary',
size: 'medium',
disabled: false
}, props);
return (
<button
type={merged.type}
class={`btn btn-${merged.variant} btn-${merged.size}`}
disabled={merged.disabled}
>
{merged.children}
</button>
);
}
4.4 使用 splitProps #
jsx
import { splitProps } from 'solid-js';
function Input(props) {
// 分离本地 props 和传递给元素的 props
const [local, others] = splitProps(props, [
'label',
'error',
'helperText'
]);
return (
<div class="input-wrapper">
<label>{local.label}</label>
<input {...others} />
{local.error && <span class="error">{local.error}</span>}
{local.helperText && <span class="helper">{local.helperText}</span>}
</div>
);
}
五、TypeScript 类型 #
5.1 基本类型定义 #
tsx
interface ButtonProps {
type?: 'button' | 'submit' | 'reset';
variant?: 'primary' | 'secondary' | 'danger';
size?: 'small' | 'medium' | 'large';
disabled?: boolean;
onClick?: () => void;
children: JSX.Element;
}
function Button(props: ButtonProps) {
return (
<button
type={props.type ?? 'button'}
class={`btn btn-${props.variant ?? 'primary'}`}
disabled={props.disabled}
onClick={props.onClick}
>
{props.children}
</button>
);
}
5.2 泛型 Props #
tsx
interface ListProps<T> {
items: T[];
renderItem: (item: T, index: number) => JSX.Element;
keyFn?: (item: T) => string | number;
}
function List<T>(props: ListProps<T>) {
return (
<ul>
<For each={props.items}>
{(item, index) => props.renderItem(item, index())}
</For>
</ul>
);
}
// 使用
<List
items={users}
renderItem={(user) => <li>{user.name}</li>}
/>
5.3 Signal 类型 #
tsx
interface CounterProps {
count: () => number; // Signal getter
setCount: (value: number) => void; // Signal setter
}
function Counter(props: CounterProps) {
return (
<div>
<p>Count: {props.count()}</p>
<button onClick={() => props.setCount(props.count() + 1)}>
Increment
</button>
</div>
);
}
5.4 组件类型 #
tsx
// 函数组件类型
type ButtonComponent = (props: ButtonProps) => JSX.Element;
const Button: ButtonComponent = (props) => {
return <button>{props.children}</button>;
};
六、高级用法 #
6.1 Props 验证 #
tsx
function validateProps<T extends Record<string, unknown>>(
props: T,
validators: Partial<Record<keyof T, (value: unknown) => boolean>>
): T {
for (const key in validators) {
const validator = validators[key];
if (validator && !validator(props[key])) {
console.warn(`Invalid prop "${key}": ${props[key]}`);
}
}
return props;
}
function UserCard(props: { name: string; age: number }) {
validateProps(props, {
name: (v) => typeof v === 'string' && v.length > 0,
age: (v) => typeof v === 'number' && v >= 0
});
return (
<div>
<h2>{props.name}</h2>
<p>Age: {props.age}</p>
</div>
);
}
6.2 Props 代理 #
jsx
function withLogging(Component) {
return (props) => {
console.log('Props:', props);
return <Component {...props} />;
};
}
const LoggedButton = withLogging(Button);
<LoggedButton label="Click" onClick={handleClick} />
6.3 动态 Props #
jsx
function DynamicProps() {
const [props, setProps] = createSignal({
color: 'red',
size: 'large'
});
return <Button {...props()} />;
}
七、常见模式 #
7.1 回调 Props #
jsx
function SearchInput(props) {
const [value, setValue] = createSignal('');
const handleChange = (e) => {
const newValue = e.target.value;
setValue(newValue);
props.onSearch?.(newValue);
};
return (
<input
value={value()}
onInput={handleChange}
placeholder={props.placeholder}
/>
);
}
// 使用
<SearchInput
placeholder="Search..."
onSearch={(query) => console.log('Search:', query)}
/>
7.2 Render Props #
jsx
function DataFetcher(props) {
const [data] = createResource(props.url);
return (
<Show when={data()} fallback={<p>Loading...</p>}>
{(d) => props.render(d())}
</Show>
);
}
// 使用
<DataFetcher
url="/api/users"
render={(users) => (
<ul>
<For each={users}>{(user) => <li>{user.name}</li>}</For>
</ul>
)}
/>
7.3 插槽模式 #
jsx
function Modal(props) {
return (
<div class="modal">
<div class="modal-header">
{props.header ?? <h3>Modal</h3>}
</div>
<div class="modal-body">
{props.children}
</div>
<div class="modal-footer">
{props.footer ?? (
<button onClick={props.onClose}>Close</button>
)}
</div>
</div>
);
}
// 使用
<Modal
header={<h2>Confirm</h2>}
footer={
<>
<button onClick={handleCancel}>Cancel</button>
<button onClick={handleConfirm}>Confirm</button>
</>
}
onClose={handleClose}
>
<p>Are you sure?</p>
</Modal>
八、最佳实践 #
8.1 Props 命名 #
jsx
// 好的命名
<Button
isLoading={loading()} // 布尔值用 is/has 前缀
onClick={handleClick} // 事件用 on 前缀
variant="primary" // 枚举用语义化名称
className="btn" // 与 HTML 属性区分
/>
// 避免的命名
<Button
loading={loading()} // 不清晰
click={handleClick} // 不符合惯例
type={1} // 魔法数字
/>
8.2 避免过度传递 #
jsx
// 避免:层层传递 props
function GrandParent() {
const data = 'data';
return <Parent data={data} />;
}
function Parent(props) {
return <Child data={props.data} />;
}
function Child(props) {
return <p>{props.data}</p>;
}
// 推荐:使用 Context
const DataContext = createContext();
function GrandParent() {
return (
<DataContext.Provider value="data">
<Parent />
</DataContext.Provider>
);
}
function Child() {
const data = useContext(DataContext);
return <p>{data}</p>;
}
8.3 Props 文档化 #
tsx
/**
* Button 组件
*
* @param props - 组件属性
* @param props.variant - 按钮样式变体
* @param props.size - 按钮尺寸
* @param props.disabled - 是否禁用
* @param props.onClick - 点击回调
* @param props.children - 按钮内容
*/
function Button(props: ButtonProps) {
// ...
}
九、总结 #
9.1 Props 核心 API #
| API | 说明 |
|---|---|
props.name |
访问属性 |
props.children |
访问子内容 |
mergeProps |
合并默认值 |
splitProps |
分离属性 |
9.2 Props 类型 #
| 类型 | 说明 |
|---|---|
| 基本类型 | string, number, boolean |
| 对象/数组 | 复杂数据结构 |
| 函数 | 回调函数 |
| Signal | 响应式状态 |
| JSX.Element | React 元素 |
9.3 最佳实践 #
- 保持只读:不修改 props
- 设置默认值:提供合理默认
- 类型定义:使用 TypeScript
- 文档化:添加 JSDoc 注释
- 避免过度传递:使用 Context
最后更新:2026-03-28