Props适配 #
一、Props 基础 #
1.1 基本概念 #
Styled Components 允许通过 Props 动态控制样式:
jsx
const Button = styled.button`
background: ${props => props.primary ? '#667eea' : '#fff'};
color: ${props => props.primary ? '#fff' : '#667eea'};
padding: 12px 24px;
border: 2px solid #667eea;
border-radius: 8px;
`;
function App() {
return (
<>
<Button primary>Primary Button</Button>
<Button>Secondary Button</Button>
</>
);
}
1.2 Props 传递机制 #
text
组件 Props 流向
┌─────────────────────────────────────────────────────────────┐
│ │
│ <Button primary size="large"> │
│ │ │ │
│ ▼ ▼ │
│ ┌─────────────────────────────────────┐ │
│ │ styled.button` │ │
│ │ background: ${p => p.primary} │ │
│ │ padding: ${p => p.size} │ │
│ │ ` │ │
│ └─────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ <button class="sc-xxx" primary size="large"> │
│ │
└─────────────────────────────────────────────────────────────┘
二、条件样式 #
2.1 三元表达式 #
jsx
const Alert = styled.div`
padding: 16px;
border-radius: 8px;
background: ${props => props.type === 'success' ? '#d4edda' :
props.type === 'warning' ? '#fff3cd' :
props.type === 'error' ? '#f8d7da' : '#cce5ff'};
color: ${props => props.type === 'success' ? '#155724' :
props.type === 'warning' ? '#856404' :
props.type === 'error' ? '#721c24' : '#004085'};
border: 1px solid ${props => props.type === 'success' ? '#c3e6cb' :
props.type === 'warning' ? '#ffeeba' :
props.type === 'error' ? '#f5c6cb' : '#b8daff'};
`;
function App() {
return (
<>
<Alert type="success">Success message</Alert>
<Alert type="warning">Warning message</Alert>
<Alert type="error">Error message</Alert>
<Alert>Info message</Alert>
</>
);
}
2.2 逻辑与运算符 #
jsx
const Card = styled.div`
padding: 24px;
background: white;
border-radius: 12px;
box-shadow: ${props => props.elevated && '0 8px 24px rgba(0, 0, 0, 0.15)'};
transform: ${props => props.elevated && 'translateY(-4px)'};
transition: all 0.3s;
`;
const Text = styled.p`
color: #333;
font-weight: ${props => props.bold && '700'};
font-style: ${props => props.italic && 'italic'};
text-decoration: ${props => props.underline && 'underline'};
`;
function App() {
return (
<>
<Card elevated>Elevated Card</Card>
<Card>Normal Card</Card>
<Text bold underline>Bold and underlined</Text>
</>
);
}
2.3 默认值处理 #
jsx
const Button = styled.button`
padding: ${props => props.padding || '12px 24px'};
font-size: ${props => props.fontSize || '16px'};
border-radius: ${props => props.radius || '8px'};
background: ${props => props.bg || '#667eea'};
color: ${props => props.color || 'white'};
`;
const Input = styled.input`
padding: ${props => props.$padding ?? '12px'};
border: ${props => props.$border ?? '2px solid #e0e0e0'};
border-radius: ${props => props.$radius ?? '8px'};
`;
三、样式变体 #
3.1 变体映射对象 #
jsx
const buttonVariants = {
primary: {
background: '#667eea',
color: 'white',
border: 'none',
},
secondary: {
background: 'transparent',
color: '#667eea',
border: '2px solid #667eea',
},
danger: {
background: '#ff4d4f',
color: 'white',
border: 'none',
},
ghost: {
background: 'transparent',
color: '#333',
border: 'none',
},
};
const Button = styled.button`
padding: 12px 24px;
border-radius: 8px;
font-size: 16px;
cursor: pointer;
transition: all 0.2s;
background: ${props => buttonVariants[props.variant]?.background || '#667eea'};
color: ${props => buttonVariants[props.variant]?.color || 'white'};
border: ${props => buttonVariants[props.variant]?.border || 'none'};
&:hover {
opacity: 0.9;
}
`;
function App() {
return (
<>
<Button variant="primary">Primary</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="danger">Danger</Button>
<Button variant="ghost">Ghost</Button>
</>
);
}
3.2 尺寸变体 #
jsx
const sizeStyles = {
small: `
padding: 8px 16px;
font-size: 14px;
border-radius: 6px;
`,
medium: `
padding: 12px 24px;
font-size: 16px;
border-radius: 8px;
`,
large: `
padding: 16px 32px;
font-size: 18px;
border-radius: 10px;
`,
};
const Button = styled.button`
${props => sizeStyles[props.size] || sizeStyles.medium}
background: #667eea;
color: white;
border: none;
cursor: pointer;
`;
function App() {
return (
<>
<Button size="small">Small</Button>
<Button size="medium">Medium</Button>
<Button size="large">Large</Button>
</>
);
}
3.3 组合变体 #
jsx
const getButtonStyles = ({ variant, size, disabled }) => {
const variants = {
primary: { bg: '#667eea', color: 'white' },
secondary: { bg: 'white', color: '#667eea' },
danger: { bg: '#ff4d4f', color: 'white' },
};
const sizes = {
small: { padding: '8px 16px', fontSize: '14px' },
medium: { padding: '12px 24px', fontSize: '16px' },
large: { padding: '16px 32px', fontSize: '18px' },
};
const v = variants[variant] || variants.primary;
const s = sizes[size] || sizes.medium;
return `
padding: ${s.padding};
font-size: ${s.fontSize};
background: ${disabled ? '#ccc' : v.bg};
color: ${disabled ? '#999' : v.color};
opacity: ${disabled ? '0.6' : '1'};
cursor: ${disabled ? 'not-allowed' : 'pointer'};
`;
};
const Button = styled.button`
${getButtonStyles}
border: none;
border-radius: 8px;
transition: all 0.2s;
&:hover:not(:disabled) {
opacity: 0.9;
transform: translateY(-1px);
}
`;
function App() {
return (
<>
<Button variant="primary" size="large">Primary Large</Button>
<Button variant="secondary" size="small">Secondary Small</Button>
<Button variant="danger" disabled>Danger Disabled</Button>
</>
);
}
四、函数式样式 #
4.1 样式函数 #
jsx
const getSpacing = (size) => {
const spacings = {
xs: '4px',
sm: '8px',
md: '16px',
lg: '24px',
xl: '32px',
};
return spacings[size] || spacings.md;
};
const Box = styled.div`
padding: ${props => getSpacing(props.p)};
margin: ${props => getSpacing(props.m)};
background: ${props => props.bg || 'white'};
`;
function App() {
return (
<>
<Box p="lg" m="md" bg="#f0f0f0">Large padding, medium margin</Box>
<Box p="sm" m="xs">Small padding, extra small margin</Box>
</>
);
}
4.2 颜色函数 #
jsx
const getColor = (colorName, shade = 'main') => {
const colors = {
primary: {
light: '#a5b4fc',
main: '#667eea',
dark: '#4c51bf',
},
success: {
light: '#86efac',
main: '#22c55e',
dark: '#16a34a',
},
danger: {
light: '#fca5a5',
main: '#ef4444',
dark: '#dc2626',
},
};
return colors[colorName]?.[shade] || colors.primary.main;
};
const Badge = styled.span`
display: inline-flex;
padding: 4px 12px;
border-radius: 20px;
font-size: 12px;
font-weight: 600;
background: ${props => getColor(props.color, 'light')};
color: ${props => getColor(props.color, 'dark')};
`;
function App() {
return (
<>
<Badge color="primary">Primary</Badge>
<Badge color="success">Success</Badge>
<Badge color="danger">Danger</Badge>
</>
);
}
4.3 响应式函数 #
jsx
const breakpoints = {
sm: '640px',
md: '768px',
lg: '1024px',
xl: '1280px',
};
const responsive = (css, breakpoint = 'md') => `
@media (min-width: ${breakpoints[breakpoint]}) {
${css}
}
`;
const Container = styled.div`
padding: 16px;
${props => props.wide && responsive(`
max-width: 1400px;
`, 'lg')}
${props => props.centered && responsive(`
margin: 0 auto;
`)}
`;
const Grid = styled.div`
display: grid;
gap: 16px;
grid-template-columns: 1fr;
${responsive(`grid-template-columns: repeat(2, 1fr);`, 'sm')}
${responsive(`grid-template-columns: repeat(3, 1fr);`, 'md')}
${responsive(`grid-template-columns: repeat(4, 1fr);`, 'lg')}
`;
五、复杂 Props 处理 #
5.1 对象类型 Props #
jsx
const Card = styled.div`
padding: ${props => props.spacing?.padding || '24px'};
margin: ${props => props.spacing?.margin || '0'};
background: ${props => props.theme?.background || 'white'};
border-radius: ${props => props.border?.radius || '12px'};
box-shadow: ${props => props.shadow?.value || '0 4px 12px rgba(0, 0, 0, 0.1)'};
`;
function App() {
return (
<Card
spacing={{ padding: '32px', margin: '16px' }}
border={{ radius: '16px' }}
shadow={{ value: '0 8px 24px rgba(0, 0, 0, 0.15)' }}
>
Styled Card
</Card>
);
}
5.2 数组类型 Props #
jsx
const Flex = styled.div`
display: flex;
flex-direction: ${props => props.direction || 'row'};
gap: ${props => props.gap?.[0] || '16px'};
@media (min-width: 768px) {
gap: ${props => props.gap?.[1] || props.gap?.[0] || '16px'};
}
@media (min-width: 1024px) {
gap: ${props => props.gap?.[2] || props.gap?.[1] || props.gap?.[0] || '16px'};
}
`;
function App() {
return (
<Flex gap={['8px', '16px', '24px']}>
<div>Item 1</div>
<div>Item 2</div>
<div>Item 3</div>
</Flex>
);
}
5.3 函数类型 Props #
jsx
const Button = styled.button`
padding: 12px 24px;
background: ${props => typeof props.bg === 'function'
? props.bg(props)
: props.bg || '#667eea'};
color: white;
border-radius: 8px;
`;
function App() {
return (
<Button
bg={(props) => props.disabled ? '#ccc' : '#667eea'}
disabled={false}
>
Dynamic Background
</Button>
);
}
六、Props 过滤 #
6.1 问题:Props 传递到 DOM #
jsx
const Button = styled.button`
background: ${props => props.primary ? '#667eea' : '#fff'};
`;
function App() {
return <Button primary>Click</Button>;
}
渲染结果(primary 被传递到 DOM):
html
<button primary="" class="sc-xxx">Click</button>
6.2 解决方案:使用 transient props #
使用 $ 前缀的 props 不会传递到 DOM:
jsx
const Button = styled.button`
background: ${props => props.$primary ? '#667eea' : '#fff'};
padding: ${props => props.$size === 'large' ? '16px 32px' : '12px 24px'};
`;
function App() {
return (
<>
<Button $primary>Primary</Button>
<Button $size="large">Large</Button>
</>
);
}
渲染结果:
html
<button class="sc-xxx">Primary</button>
<button class="sc-xxx">Large</button>
6.3 解决方案:shouldForwardProp #
jsx
const Button = styled.button.withConfig({
shouldForwardProp: (prop) => !['primary', 'size', 'variant'].includes(prop),
})`
background: ${props => props.primary ? '#667eea' : '#fff'};
padding: ${props => props.size === 'large' ? '16px 32px' : '12px 24px'};
`;
function App() {
return <Button primary size="large">Click</Button>;
}
6.4 使用正则表达式过滤 #
jsx
const Input = styled.input.withConfig({
shouldForwardProp: (prop) => !prop.startsWith('$'),
})`
padding: 12px;
border: 2px solid ${props => props.$borderColor || '#e0e0e0'};
border-radius: ${props => props.$borderRadius || '8px'};
`;
const Card = styled.div.withConfig({
shouldForwardProp: (prop) => !['elevated', 'rounded', 'padded'].includes(prop),
})`
background: white;
box-shadow: ${props => props.elevated && '0 8px 24px rgba(0, 0, 0, 0.15)'};
border-radius: ${props => props.rounded ? '16px' : '4px'};
padding: ${props => props.padded && '24px'};
`;
七、TypeScript 类型定义 #
7.1 基本类型 #
tsx
import styled from 'styled-components';
interface ButtonProps {
primary?: boolean;
size?: 'small' | 'medium' | 'large';
disabled?: boolean;
}
const Button = styled.button<ButtonProps>`
padding: ${props =>
props.size === 'small' ? '8px 16px' :
props.size === 'large' ? '16px 32px' :
'12px 24px'
};
background: ${props => props.primary ? '#667eea' : '#fff'};
color: ${props => props.primary ? '#fff' : '#667eea'};
opacity: ${props => props.disabled ? 0.6 : 1};
`;
function App() {
return (
<>
<Button primary size="large">Primary Large</Button>
<Button size="small">Secondary Small</Button>
</>
);
}
7.2 扩展 HTML 元素属性 #
tsx
import styled from 'styled-components';
interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
$borderColor?: string;
$borderRadius?: string;
}
const Input = styled.input<InputProps>`
padding: 12px 16px;
border: 2px solid ${props => props.$borderColor || '#e0e0e0'};
border-radius: ${props => props.$borderRadius || '8px'};
&:focus {
border-color: #667eea;
outline: none;
}
`;
function App() {
return (
<Input
$borderColor="#667eea"
placeholder="Enter text"
type="text"
/>
);
}
八、性能优化 #
8.1 避免内联函数 #
jsx
const Button = styled.button`
padding: ${props => {
return props.size === 'large' ? '16px 32px' : '12px 24px';
}};
`;
const getSizePadding = (size) => {
switch (size) {
case 'large': return '16px 32px';
case 'small': return '8px 16px';
default: return '12px 24px';
}
};
const OptimizedButton = styled.button`
padding: ${props => getSizePadding(props.size)};
`;
8.2 使用 useMemo 缓存样式 #
jsx
import { useMemo } from 'react';
import styled, { css } from 'styled-components';
const dynamicStyle = css`
padding: ${props => props.padding};
margin: ${props => props.margin};
`;
const Box = styled.div`
${dynamicStyle}
background: white;
`;
function App({ padding, margin }) {
const boxProps = useMemo(() => ({
padding,
margin,
}), [padding, margin]);
return <Box {...boxProps} />;
}
九、总结 #
Props 适配技巧速查表:
| 技巧 | 示例 |
|---|---|
| 三元表达式 | ${p => p.primary ? 'blue' : 'white'} |
| 逻辑与 | ${p => p.bold && '700'} |
| 默认值 | ${p => p.size || '16px'} |
| 变体映射 | ${p => variants[p.variant]} |
| transient props | ${p => p.$primary} |
| shouldForwardProp | 过滤非样式 props |
下一步:学习 样式继承 掌握组件样式复用技巧。
最后更新:2026-03-28