likes
comments
collection
share

css之styled-components

作者站长头像
站长
· 阅读数 21

前言

  • 安装: npm install --save styled-components
  • 与 TypeScript 配合使用: npm install @types/styled-components -D
  • VsCode插件(vscode-styled-components): 自动进行 CSS 高亮、补全、纠正等

基本使用

  • 使用注意点: styled-components 定义需要写在 render 外,否则它将在每个渲染过程中重新创建。在 render 方法中定义样式化的组件将阻碍缓存并大大降低渲染速度,因此应避免使用
import React from 'react'
import styled from 'styled-components'

// 写在组件外部
const ScH1 = styled.h1`
  color: red;
  background-color: blue;
  text-align: center;
  padding: 10px;
  .divBox: {
    padding: 10px;
  }
`

export default () => {
  return (
    <ScH1>
      <div className="divBox">
        Hello World
      </div>
    </ScH1>
  )
}

使用伪元素、选择器、嵌套语法

const ScDiv = styled.div`
  color: blue;

  &:hover {
    color: red; // 被 hover 时的样式
  }

  & ~ & {
    background: tomato; // ScDiv 作为 ScDiv 的 sibling
  }

  & + & {
    background: lime; // 与 ScDiv 相邻的 ScDiv
  }

  &.something {
    background: orange; // 带有 class .something 的 ScDiv
  }

  .something-child & {
    border: 1px solid; // 不带有 & 时指向子元素,因此这里表示在带有 class .something-child 之内的 ScDiv
  }
`

render(
  <React.Fragment>
    <ScDiv>Hello world!</ScDiv>
    <ScDiv>How ya doing?</ScDiv>
    <ScDiv className="something">The sun is shining...</ScDiv>
    <ScDiv>Pretty nice day today.</ScDiv>
    <ScDiv>Don't you think?</ScDiv>
    <div className="something-else">
      <ScDiv>Splendid.</ScDiv>
    </div>
  </React.Fragment>
)

动画

const rotate = keyframes`
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
`

const Rotate = styled.div`
  display: inline-block;
  animation: ${rotate} 2s linear infinite;
  padding: 2rem 1rem;
  font-size: 1.2rem;
`

render(<Rotate>&lt; 💅🏾 &gt;</Rotate>)

媒体查询

  • 普通写法
const Content = styled.div`
  background: papayawhip;
  height: 3em;
  width: 3em;

  @media (max-width: 700px) {
    background: palevioletred;
  }
`

render(
  <Content />
)
  • 封装版
const sizes = {
  desktop: 992,
  tablet: 768,
  phone: 576,
};

const media = Object.keys(sizes).reduce((acc, label) => {
  acc[label] = (...args) => css`
    @media (max-width: ${sizes[label] / 16}em) {
      ${css(...args)}
    }
  `;
  return acc;
}, {});

const Content = styled.div`
  height: 3em;
  width: 3em;
  background: papayawhip;
  ${media.desktop`background: dodgerblue;`}
  ${media.tablet`background: mediumseagreen;`}
  ${media.phone`background: palevioletred;`}
`;

render(<Content />);

动态样式

const ScButton = styled.button `
  background: ${props => props.primary ? "blue" : "white"};
  color: ${props => props.primary ? "white" : "blue"};
  border: 2px solid palevioletred;
  border-radius: 3px;
  padding: 0.25em 1em;
`

export default () => {
  return (
    <div>
      <Button>Normal</Button>
      {/* 通过 props 实现动态样式 */}
      <Button primary>Primary</Button>
    </div>
  )
}

样式继承与重用

const ScButton = styled.button`
  color: white;
  background-color: blue;
  border: 2px solid palevioletred;
  border-radius: 3px;
  padding: 0.25em 1em;
`

继承

// 创建一个继承 ScButton 的新组件 ScExtendedButton
const ScExtendedButton = styled(ScButton)`
  color: blue;
  background-color: white;
  margin-top: 1em;
`

重用

export default () => {
  return (
    // as(可以是组件名,也可以是普通标签名): 表示要渲染出来的标签或组件
    // 这个例子表示: 继承了 ScExtendedButton 样式的 a 标签
    <ScExtendedButton as="a" href="#">
      Extends Link with Button styles
    </ScExtendedButton>
  )
}

样式化任意组件

样式化组件

const Link = ({ className, children }) => (
  // className 属性附加到 DOM 元素上
  <a className={className}>
    {children}
  </a>
)

const StyledLink = styled(Link)`
  color: red;
  font-weight: bold;
`

render(
  <div>
    <Link>Unstyled Link</Link>
    <StyledLink>Styled Link</StyledLink>
  </div>
)

样式化第三方组件

import { Button } from '@alifd/next'

const ScButton = styled(Button)`
  margin-top: 12px;
  color: green;
`

render(
  <div>
    <ScButton>Styled Fusion Button</ScButton>
  </div>
)

通过 .attrs 附加 props

// .attrs允许附加静态或动态的 props
const Input = styled.input.attrs((props) => ({
  type: "password", // 定义静态的 prop
  size: props.size || "1em", // 定义动态的 prop
}))`
  color: palevioletred;
  font-size: 1em;
  border: 2px solid palevioletred;
  border-radius: 3px;
  /* 最终的 props 是合并 attrs 返回值的 props 的结果 */
  margin: ${(props) => props.size};
  padding: ${(props) => props.size};
`

render(
  <div>
    <Input placeholder="A small text input" />
    <Input placeholder="A bigger text input" size="2em" />
  </div>
)

主题切换

// 通过使用 props.theme 可以访问到 ThemeProvider 传递下来的对象
const Button = styled.button`
  font-size: 1em;
  margin: 1em;
  padding: 0.25em 1em;
  border-radius: 3px;
  color: ${props => props.theme.main};
  border: 2px solid ${props => props.theme.main};
`

// 为 Button 指定默认主题
Button.defaultProps = {
  theme: {
    main: "palevioletred"
  }
}

const theme = {
  main: "mediumseagreen"
}

render(
  <div>
    <Button>Normal</Button>
    // 采用了 ThemeProvider 提供的主题的 Button
    <ThemeProvider theme={theme}>
      <Button>Themed</Button>
    </ThemeProvider>
  </div>
)

CSS Prop实现内联样式与mixin

需要用到 styled-components 提供的 babel-plugin: styled-components.com/docs/toolin…

内联样式

<div
  css={`
    background: papayawhip;
    color: ${props => props.theme.colors.text};
  `}
/>

<MyComponent css="padding: 0.5em 1em;"/>

mixin

import styled, { css } from 'styled-components';
import { Button as FusionButton } from '@alifd/next';

const mixinCommonCSS = css`
  margin-top: 12px;
  border: 1px solid grey;
  borde-radius: 4px;
`;

const ScButton = styled.button`
  ${mixinCommonCSS}
  color: yellow;
`;

const ScFusionButton = styled(FusionButton)`
  ${mixinCommonCSS}
  color: blue;
`;

参考