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