样式对象 #

一、样式对象基础 #

1.1 基本语法 #

jsx
import styled from 'styled-components';

const Button = styled.button({
  padding: '12px 24px',
  fontSize: '16px',
  fontWeight: '600',
  backgroundColor: '#667eea',
  color: '#ffffff',
  border: 'none',
  borderRadius: '8px',
  cursor: 'pointer',
});

1.2 与模板字符串对比 #

jsx
const ButtonTemplate = styled.button`
  padding: 12px 24px;
  font-size: 16px;
  background-color: #667eea;
  color: #ffffff;
  border: none;
  border-radius: 8px;
  cursor: pointer;
`;

const ButtonObject = styled.button({
  padding: '12px 24px',
  fontSize: '16px',
  backgroundColor: '#667eea',
  color: '#ffffff',
  border: 'none',
  borderRadius: '8px',
  cursor: 'pointer',
});

1.3 属性命名规则 #

text
CSS 属性命名转换

CSS 短横线命名 → JavaScript 驼峰命名

background-color  → backgroundColor
border-radius     → borderRadius
font-size         → fontSize
line-height       → lineHeight
box-shadow        → boxShadow
text-align        → textAlign
z-index           → zIndex
flex-direction    → flexDirection
justify-content   → justifyContent
align-items       → alignItems

二、嵌套选择器 #

2.1 伪类选择器 #

jsx
const Button = styled.button({
  padding: '12px 24px',
  backgroundColor: '#667eea',
  color: '#ffffff',
  border: 'none',
  borderRadius: '8px',
  cursor: 'pointer',
  transition: 'all 0.2s',
  
  ':hover': {
    backgroundColor: '#5a6fd6',
    transform: 'translateY(-2px)',
  },
  
  ':active': {
    transform: 'translateY(0)',
  },
  
  ':focus': {
    outline: 'none',
    boxShadow: '0 0 0 3px rgba(102, 126, 234, 0.3)',
  },
  
  ':disabled': {
    backgroundColor: '#cccccc',
    cursor: 'not-allowed',
    transform: 'none',
  },
});

2.2 伪元素选择器 #

jsx
const Tooltip = styled.span({
  position: 'relative',
  cursor: 'pointer',
  
  '::before': {
    content: 'attr(data-tooltip)',
    position: 'absolute',
    bottom: '100%',
    left: '50%',
    transform: 'translateX(-50%)',
    padding: '8px 12px',
    backgroundColor: '#333333',
    color: '#ffffff',
    fontSize: '12px',
    borderRadius: '4px',
    whiteSpace: 'nowrap',
    opacity: '0',
    visibility: 'hidden',
    transition: 'all 0.2s',
  },
  
  ':hover::before': {
    opacity: '1',
    visibility: 'visible',
  },
});

2.3 后代选择器 #

jsx
const Card = styled.div({
  padding: '24px',
  backgroundColor: '#ffffff',
  borderRadius: '12px',
  boxShadow: '0 4px 12px rgba(0, 0, 0, 0.1)',
  
  h2: {
    fontSize: '24px',
    fontWeight: '700',
    marginBottom: '12px',
    color: '#1a1a2e',
  },
  
  p: {
    fontSize: '16px',
    lineHeight: '1.6',
    color: '#666666',
    marginBottom: '16px',
  },
  
  a: {
    color: '#667eea',
    textDecoration: 'none',
    
    ':hover': {
      textDecoration: 'underline',
    },
  },
  
  'ul, ol': {
    paddingLeft: '20px',
    marginBottom: '16px',
  },
});

2.4 子代选择器 #

jsx
const List = styled.ul({
  listStyle: 'none',
  padding: '0',
  margin: '0',
  
  '> li': {
    padding: '12px',
    borderBottom: '1px solid #eeeeee',
    
    ':last-child': {
      borderBottom: 'none',
    },
  },
  
  '> li > a': {
    color: '#333333',
    textDecoration: 'none',
  },
});

三、媒体查询 #

3.1 基本媒体查询 #

jsx
const Container = styled.div({
  padding: '16px',
  width: '100%',
  
  '@media (min-width: 768px)': {
    padding: '24px',
    maxWidth: '720px',
    margin: '0 auto',
  },
  
  '@media (min-width: 1024px)': {
    padding: '32px',
    maxWidth: '960px',
  },
  
  '@media (min-width: 1280px)': {
    maxWidth: '1140px',
  },
});

3.2 响应式网格 #

jsx
const Grid = styled.div({
  display: 'grid',
  gap: '16px',
  gridTemplateColumns: '1fr',
  
  '@media (min-width: 640px)': {
    gridTemplateColumns: 'repeat(2, 1fr)',
  },
  
  '@media (min-width: 1024px)': {
    gridTemplateColumns: 'repeat(3, 1fr)',
    gap: '24px',
  },
  
  '@media (min-width: 1280px)': {
    gridTemplateColumns: 'repeat(4, 1fr)',
  },
});

3.3 媒体查询变量 #

jsx
const breakpoints = {
  sm: '640px',
  md: '768px',
  lg: '1024px',
  xl: '1280px',
};

const Container = styled.div({
  padding: '16px',
  
  [`@media (min-width: ${breakpoints.md})`]: {
    padding: '24px',
  },
  
  [`@media (min-width: ${breakpoints.lg})`]: {
    padding: '32px',
  },
});

四、函数样式 #

4.1 基于 Props 的样式 #

jsx
const Button = styled.button(props => ({
  padding: '12px 24px',
  fontSize: '16px',
  borderRadius: '8px',
  cursor: 'pointer',
  border: 'none',
  transition: 'all 0.2s',
  
  backgroundColor: props.$primary ? '#667eea' : '#ffffff',
  color: props.$primary ? '#ffffff' : '#667eea',
  
  ...(props.$disabled && {
    opacity: 0.6,
    cursor: 'not-allowed',
  }),
  
  ...(props.$fullWidth && {
    width: '100%',
  }),
}));

4.2 复杂条件样式 #

jsx
const sizes = {
  small: {
    padding: '8px 16px',
    fontSize: '14px',
  },
  medium: {
    padding: '12px 24px',
    fontSize: '16px',
  },
  large: {
    padding: '16px 32px',
    fontSize: '18px',
  },
};

const variants = {
  primary: {
    backgroundColor: '#667eea',
    color: '#ffffff',
    border: 'none',
  },
  secondary: {
    backgroundColor: 'transparent',
    color: '#667eea',
    border: '2px solid #667eea',
  },
  danger: {
    backgroundColor: '#ff4d4f',
    color: '#ffffff',
    border: 'none',
  },
};

const Button = styled.button(props => ({
  borderRadius: '8px',
  cursor: 'pointer',
  transition: 'all 0.2s',
  
  ...sizes[props.$size || 'medium'],
  ...variants[props.$variant || 'primary'],
  
  ':hover:not(:disabled)': {
    opacity: 0.9,
    transform: 'translateY(-1px)',
  },
  
  ...(props.$disabled && {
    opacity: 0.6,
    cursor: 'not-allowed',
  }),
}));

4.3 主题访问 #

jsx
const Card = styled.div(props => ({
  padding: props.theme.spacing[6],
  backgroundColor: props.theme.colors.background,
  borderRadius: props.theme.borderRadius.lg,
  boxShadow: props.theme.shadows.md,
  
  h2: {
    fontSize: props.theme.typography.fontSize['2xl'],
    fontWeight: props.theme.typography.fontWeight.bold,
    color: props.theme.colors.text,
    marginBottom: props.theme.spacing[4],
  },
  
  p: {
    fontSize: props.theme.typography.fontSize.base,
    lineHeight: props.theme.typography.lineHeight.relaxed,
    color: props.theme.colors.textSecondary,
  },
}));

五、样式对象复用 #

5.1 基础样式对象 #

jsx
const baseButtonStyles = {
  display: 'inline-flex',
  alignItems: 'center',
  justifyContent: 'center',
  fontWeight: '600',
  borderRadius: '8px',
  cursor: 'pointer',
  transition: 'all 0.2s',
};

const PrimaryButton = styled.button({
  ...baseButtonStyles,
  padding: '12px 24px',
  backgroundColor: '#667eea',
  color: '#ffffff',
  border: 'none',
});

const SecondaryButton = styled.button({
  ...baseButtonStyles,
  padding: '12px 24px',
  backgroundColor: 'transparent',
  color: '#667eea',
  border: '2px solid #667eea',
});

5.2 样式变体 #

jsx
const buttonVariants = {
  primary: {
    backgroundColor: '#667eea',
    color: '#ffffff',
    border: 'none',
  },
  secondary: {
    backgroundColor: 'transparent',
    color: '#667eea',
    border: '2px solid #667eea',
  },
  danger: {
    backgroundColor: '#ff4d4f',
    color: '#ffffff',
    border: 'none',
  },
};

const buttonSizes = {
  small: {
    padding: '8px 16px',
    fontSize: '14px',
  },
  medium: {
    padding: '12px 24px',
    fontSize: '16px',
  },
  large: {
    padding: '16px 32px',
    fontSize: '18px',
  },
};

const Button = styled.button(props => ({
  borderRadius: '8px',
  cursor: 'pointer',
  transition: 'all 0.2s',
  
  ...buttonSizes[props.$size || 'medium'],
  ...buttonVariants[props.$variant || 'primary'],
}));

5.3 混合样式 #

jsx
const flexCenter = {
  display: 'flex',
  alignItems: 'center',
  justifyContent: 'center',
};

const cardBase = {
  backgroundColor: '#ffffff',
  borderRadius: '12px',
  boxShadow: '0 4px 12px rgba(0, 0, 0, 0.1)',
};

const smoothTransition = {
  transition: 'all 0.3s ease',
};

const Card = styled.div({
  ...cardBase,
  ...smoothTransition,
  padding: '24px',
  
  ':hover': {
    transform: 'translateY(-4px)',
    boxShadow: '0 8px 24px rgba(0, 0, 0, 0.15)',
  },
});

const CenteredCard = styled.div({
  ...cardBase,
  ...flexCenter,
  ...smoothTransition,
  padding: '24px',
  minHeight: '200px',
});

六、TypeScript 支持 #

6.1 类型定义 #

tsx
import styled from 'styled-components';
import { CSSObject } from 'styled-components';

interface ButtonProps {
  $variant?: 'primary' | 'secondary' | 'danger';
  $size?: 'small' | 'medium' | 'large';
}

const buttonStyles: CSSObject = {
  padding: '12px 24px',
  borderRadius: '8px',
  cursor: 'pointer',
  transition: 'all 0.2s',
};

const Button = styled.button<ButtonProps>(props => ({
  ...buttonStyles,
  backgroundColor: props.$variant === 'danger' ? '#ff4d4f' : '#667eea',
  color: '#ffffff',
  border: 'none',
}));

6.2 完整类型示例 #

tsx
import styled, { CSSObject, DefaultTheme } from 'styled-components';

type StyleFunction<P = {}> = (props: P & { theme: DefaultTheme }) => CSSObject;

interface CardProps {
  $elevated?: boolean;
  $rounded?: boolean;
}

const cardStyles: StyleFunction<CardProps> = (props) => ({
  padding: props.theme.spacing[6],
  backgroundColor: props.theme.colors.background,
  borderRadius: props.$rounded ? '16px' : '8px',
  boxShadow: props.$elevated 
    ? '0 8px 24px rgba(0, 0, 0, 0.15)' 
    : '0 4px 12px rgba(0, 0, 0, 0.1)',
});

const Card = styled.div<CardProps>(cardStyles);

七、与模板字符串混合 #

7.1 混合使用 #

jsx
import styled, { css } from 'styled-components';

const baseStyles = {
  padding: '12px 24px',
  borderRadius: '8px',
  cursor: 'pointer',
};

const hoverStyles = css`
  &:hover {
    opacity: 0.9;
    transform: translateY(-1px);
  }
`;

const Button = styled.button`
  ${baseStyles}
  ${hoverStyles}
  background: #667eea;
  color: white;
  border: none;
`;

7.2 对象转 css #

jsx
import styled, { css } from 'styled-components';

const objectToCss = (obj) => css`${obj}`;

const styles = {
  padding: '12px',
  backgroundColor: '#667eea',
};

const Button = styled.button`
  ${objectToCss(styles)}
  color: white;
  border: none;
`;

八、性能考虑 #

8.1 对象 vs 模板字符串 #

jsx
const ObjectButton = styled.button({
  padding: '12px 24px',
  backgroundColor: '#667eea',
  color: '#ffffff',
  border: 'none',
  borderRadius: '8px',
});

const TemplateButton = styled.button`
  padding: 12px 24px;
  background-color: #667eea;
  color: #ffffff;
  border: none;
  border-radius: 8px;
`;

性能对比:

text
对象样式
├── 更快的解析速度
├── 更好的类型推断
├── 适合静态样式
└── 函数样式每次渲染都会重新计算

模板字符串
├── 更好的语法高亮
├── 支持完整 CSS 语法
├── 适合复杂选择器
└── 更接近原生 CSS 写法

8.2 缓存样式对象 #

jsx
const staticStyles = {
  padding: '12px 24px',
  borderRadius: '8px',
};

const Button = styled.button(props => ({
  ...staticStyles,
  backgroundColor: props.$primary ? '#667eea' : '#ffffff',
}));

九、选择指南 #

9.1 使用对象样式的场景 #

text
✅ 推荐使用对象样式
├── 简单静态样式
├── 需要类型推断
├── 样式需要动态计算
├── 与 JavaScript 逻辑紧密耦合
└── 需要样式对象复用

9.2 使用模板字符串的场景 #

text
✅ 推荐使用模板字符串
├── 复杂 CSS 选择器
├── 需要嵌套语法
├── 大量伪类/伪元素
├── 复杂媒体查询
├── 需要 CSS 注释
└── 希望更好的语法高亮

十、总结 #

样式对象要点速查表:

特性 对象语法 模板字符串
属性命名 驼峰命名 短横线命名
嵌套选择器 ':hover': {} &:hover {}
媒体查询 '@media ...': {} @media ... {}
Props 函数返回对象 插值表达式
类型支持 天然支持 需要配置

下一步:学习 组件组合模式 掌握组件样式的组合技巧。

最后更新:2026-03-28