Styled组件 #

基本概念 #

styled API 是 Emotion 提供的一种创建样式化组件的方式,语法与 styled-components 类似,让你可以创建带有样式的 React 组件。

基本用法 #

创建样式组件 #

jsx
import styled from '@emotion/styled'

const Button = styled.button`
  padding: 12px 24px;
  background-color: #007bff;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  font-size: 16px;
  
  &:hover {
    background-color: #0056b3;
  }
`

function App() {
  return <Button>Click me</Button>
}

支持的元素 #

styled 支持所有 HTML 元素:

jsx
import styled from '@emotion/styled'

const Container = styled.div`...`
const Title = styled.h1`...`
const Paragraph = styled.p`...`
const Input = styled.input`...`
const Link = styled.a`...`
const Image = styled.img`...`
const List = styled.ul`...`
const ListItem = styled.li`...`

Props 动态样式 #

基本用法 #

根据 props 动态设置样式:

jsx
import styled from '@emotion/styled'

const Button = styled.button`
  padding: ${props => props.size === 'large' ? '16px 32px' : '8px 16px'};
  font-size: ${props => props.size === 'large' ? '18px' : '14px'};
  background-color: ${props => props.variant === 'primary' ? '#007bff' : 
                              props.variant === 'danger' ? '#dc3545' : '#6c757d'};
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
`

function App() {
  return (
    <>
      <Button variant="primary" size="large">Primary Large</Button>
      <Button variant="danger" size="small">Danger Small</Button>
      <Button>Default</Button>
    </>
  )
}

复杂逻辑 #

使用函数处理复杂的样式逻辑:

jsx
import styled from '@emotion/styled'

const getButtonColors = (variant) => {
  const colors = {
    primary: { bg: '#007bff', hover: '#0056b3' },
    secondary: { bg: '#6c757d', hover: '#545b62' },
    success: { bg: '#28a745', hover: '#1e7e34' },
    danger: { bg: '#dc3545', hover: '#c82333' },
    warning: { bg: '#ffc107', hover: '#e0a800' },
  }
  return colors[variant] || colors.secondary
}

const Button = styled.button`
  padding: 10px 20px;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  font-weight: bold;
  transition: background-color 0.2s ease;
  
  background-color: ${props => getButtonColors(props.variant).bg};
  color: ${props => props.variant === 'warning' ? '#333' : 'white'};
  
  &:hover {
    background-color: ${props => getButtonColors(props.variant).hover};
  }
`

应该转发 vs 不应该转发的 Props #

默认情况下,所有 props 都会传递给 DOM 元素。使用 shouldForwardProp 控制:

jsx
import styled from '@emotion/styled'

const Button = styled.button`
  padding: ${props => props.$size === 'large' ? '16px 32px' : '8px 16px'};
  background: ${props => props.$variant === 'primary' ? '#007bff' : '#6c757d'};
`, {
  shouldForwardProp: (prop) => !prop.startsWith('$')
})

function App() {
  return <Button $variant="primary" $size="large">Click</Button>
}

样式继承 #

扩展样式 #

基于已有组件扩展样式:

jsx
import styled from '@emotion/styled'

const Button = styled.button`
  padding: 10px 20px;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  font-size: 14px;
  transition: all 0.2s ease;
`

const PrimaryButton = styled(Button)`
  background-color: #007bff;
  color: white;
  
  &:hover {
    background-color: #0056b3;
  }
`

const DangerButton = styled(Button)`
  background-color: #dc3545;
  color: white;
  
  &:hover {
    background-color: #c82333;
  }
`

const OutlineButton = styled(Button)`
  background-color: transparent;
  border: 2px solid #007bff;
  color: #007bff;
  
  &:hover {
    background-color: #007bff;
    color: white;
  }
`

覆盖样式 #

在扩展时覆盖原有样式:

jsx
import styled from '@emotion/styled'

const Card = styled.div`
  padding: 20px;
  background: white;
  border-radius: 8px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
`

const FeaturedCard = styled(Card)`
  border: 2px solid gold;
  box-shadow: 0 4px 8px rgba(255, 215, 0, 0.3);
  
  &:hover {
    box-shadow: 0 8px 16px rgba(255, 215, 0, 0.4);
  }
`

样式化已有组件 #

样式化第三方组件 #

为第三方组件添加样式:

jsx
import styled from '@emotion/styled'
import { Link } from 'react-router-dom'

const StyledLink = styled(Link)`
  color: #007bff;
  text-decoration: none;
  padding: 8px 16px;
  border-radius: 4px;
  
  &:hover {
    background-color: #f0f0f0;
  }
  
  &.active {
    background-color: #007bff;
    color: white;
  }
`

样式化自定义组件 #

jsx
import styled from '@emotion/styled'

const CustomButton = ({ className, children, onClick }) => (
  <button className={className} onClick={onClick}>
    {children}
  </button>
)

const StyledCustomButton = styled(CustomButton)`
  padding: 12px 24px;
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  color: white;
  border: none;
  border-radius: 25px;
  cursor: pointer;
  font-weight: bold;
  
  &:hover {
    opacity: 0.9;
  }
`

as 属性 #

动态改变标签 #

使用 as 属性动态改变渲染的标签:

jsx
import styled from '@emotion/styled'

const Button = styled.button`
  padding: 10px 20px;
  background-color: #007bff;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
`

function App() {
  return (
    <>
      <Button>Button</Button>
      <Button as="a" href="/link">Link</Button>
      <Button as={Link} to="/route">Router Link</Button>
    </>
  )
}

创建多态组件 #

jsx
import styled from '@emotion/styled'

const Text = styled.span`
  font-size: ${props => props.size || '14px'};
  font-weight: ${props => props.weight || 'normal'};
  color: ${props => props.color || '#333'};
`

function App() {
  return (
    <>
      <Text>Span</Text>
      <Text as="p" size="16px">Paragraph</Text>
      <Text as="h1" size="32px" weight="bold">Heading</Text>
      <Text as="label" size="12px" color="#666">Label</Text>
    </>
  )
}

withComponent #

创建变体组件 #

使用 withComponent 基于相同样式创建不同标签的组件:

jsx
import styled from '@emotion/styled'

const Button = styled.button`
  padding: 10px 20px;
  background-color: #007bff;
  color: white;
  border-radius: 4px;
  text-decoration: none;
  display: inline-block;
`

const Link = Button.withComponent('a')

function App() {
  return (
    <>
      <Button>Button</Button>
      <Link href="/link">Link</Link>
    </>
  )
}

组合样式 #

使用 css 函数 #

组合多个样式:

jsx
import styled from '@emotion/styled'
import { css } from '@emotion/react'

const flexCenter = css`
  display: flex;
  align-items: center;
  justify-content: center;
`

const cardStyle = css`
  background: white;
  border-radius: 8px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
`

const Card = styled.div`
  ${flexCenter}
  ${cardStyle}
  padding: 20px;
  min-height: 200px;
`

Mixin 模式 #

创建可复用的样式片段:

jsx
import styled from '@emotion/styled'
import { css } from '@emotion/react'

const mixins = {
  flexCenter: css`
    display: flex;
    align-items: center;
    justify-content: center;
  `,
  
  truncate: css`
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  `,
  
  cardShadow: css`
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
  `,
  
  smoothTransition: css`
    transition: all 0.2s ease;
  `,
}

const Card = styled.div`
  ${mixins.cardShadow}
  ${mixins.smoothTransition}
  padding: 20px;
  background: white;
  border-radius: 8px;
  
  &:hover {
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
  }
`

const TruncatedText = styled.p`
  ${mixins.truncate}
  max-width: 200px;
`

标签模板字面量 #

函数调用形式 #

除了模板字符串,还可以使用函数调用:

jsx
import styled from '@emotion/styled'

const Button = styled.button({
  padding: '10px 20px',
  backgroundColor: '#007bff',
  color: 'white',
  border: 'none',
  borderRadius: '4px',
  cursor: 'pointer',
})

混合使用 #

jsx
import styled from '@emotion/styled'

const Button = styled.button(
  {
    padding: '10px 20px',
    border: 'none',
    borderRadius: '4px',
    cursor: 'pointer',
  },
  props => ({
    backgroundColor: props.primary ? '#007bff' : '#6c757d',
    color: 'white',
  })
)

最佳实践 #

1. 组件命名 #

使用语义化的组件名称:

jsx
const PrimaryButton = styled.button`...`
const CardContainer = styled.div`...`
const NavigationBar = styled.nav`...`
const UserAvatar = styled.img`...`

2. 样式组织 #

将相关组件放在一起:

jsx
const Card = styled.div`...`
const CardHeader = styled.div`...`
const CardBody = styled.div`...`
const CardFooter = styled.div`...`

3. 导出组件 #

从单独的文件导出样式组件:

jsx
export const Button = styled.button`...`
export const Card = styled.div`...`
export const Input = styled.input`...`

下一步 #

掌握了 styled API 后,继续学习 Props动态样式,深入了解动态样式的更多技巧。

最后更新:2026-03-28