阅读 385

styled-components

前言

React/Vue/Angular等框架出现之前的前端是html+css+js来渲染和实现页面交互,那时候更关注的点是分离,尽量不要耦合使结构更清晰

<div style="width:100px;height:32px" onclick="alert('hello world')">hello world</div>
复制代码

这样出现三大件在同一段代码里面是被认为极度不规范的,但框架出现以后采用组件化的开发方式,为了封装结构、样式、逻辑,实现组件隔离和复用,使调用者更方便使用,这种组件化的方式反而逐渐成为主流

const Index = ({width,height,onClick,children})=>{
    const style = {
        width: `${width}px`,
        height: `${height}px`,
    }
    return (<div onClick={onClick}>{children}</div>)
}
复制代码

react使用jsx对html进行封装,通过来操作html和css,约定对css带“-”的属性采用驼峰的约定。css的样式规则是对整个页面生效的,在单页面中相当于是全局的,由于对css的封装非常脆弱,以及命名冲突等众多问题,出现许多 css in js 的三方库和css modules的解决方案,之所以能使css做到局部生效,看起来具有模块化,实质是通过唯一类名来实现,或通过一些打包工具的插件生成唯一hash类名。本文重点讲的 styled-components 算是css-in-js中最热门的一个库。

image.png image.png

安装

npm install --save styled-components
复制代码

vscode插件:vscode-styled-components

举例

  • 对象写法和模板字符串写法(鼓励使用模板字符串写法,因为CSS语法支持伪选择器、媒体查询、嵌套等,而对象语法不支持,它们会被视为内联样式)
import styled from 'styled-components'

const Box1 = styled.div({
  background: 'yellow',
  height: '50px',
  width: '50px',
});

const Box2 = styled.div`
  background:blue;
  height:50px;
  width:50px;
`
复制代码
  • 通过继承和父级参数生成基本的主题组件
import styled from 'styled-components'

const LoopBasic = styled.div`
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
`

// 实现继承
const LoopOuter = styled(LoopBasic)`
  width: 24px;
  height: 24px;
  background: #ffffff;
  border: 1px solid ${props => props.color};
`

const LoopInner = styled(LoopBasic)`
  width: 18px;
  height: 18px;
  background: ${props => props.color};
  cursor: pointer;
`

const Index = ({color = '#FFBB00'}) => {
    return (
        <LoopOuter color={color}><LoopInner color={color} /></LoopOuter>
    )
}
复制代码

image.png

  • 实现css常用的旋转动画
import styled ,{ keyframes } from "styled-components"

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;
`;

const Index = () => {
    return (<Rotate>&lt; 💅🏾 &gt;</Rotate>)
}
复制代码
  • 样式类和一个样式化的组件类应用在一起,结果是样式组件类优先于样式类,因为默认情况下,样式组件在运行时将其样式注入到head标签的末尾。因此,它的样式胜过其他单一的类名选择器。
.test {
  background: blue;
}

---------------
import styled from 'styled-components'

const Test1 = styled.div`
  background: red;
  width: 100px;
  height: 100px;
`

const Test2 = styled.div`
  width: 100px;
  height: 100px;
`
const Index = () => {
  return (
    <>
      <Test1 className={styles.test} /> {/**红色 */}
      <Test2 className={styles.test} /> {/**蓝色 */}
    </>
  )
}
复制代码
  • 通过css助手实现css复用
import styled, { css } from 'styled-components'

const complexMixin = css`
  color: ${props => (props.whiteColor ? 'white' : 'black')};
`

const StyledComp = styled.div`
  ${props => (props.complex ? complexMixin : 'color: blue;')};
`

复制代码
  • 通过组件的as属性实现标签的多态
import React, { FC } from 'react'
import styled from 'styled-components'

const Component = styled.div`
  color: red;
`;

const Index = () => {
  return (
    <Component
      as="button"
      onClick={() => alert('It works!')}
    >
      Hello World!
    </Component>
  )
}

export default Index
复制代码

image.png

  • 通过ThemeProvider实现主题(react组件并不能拿到主题的参数,仅通过styled生成的组件才能拿到;可以通过withTheme高阶助手来包裹拿到顶层主题,或者使用ThemeConsumer来消费)
import React from 'react'
import styled, { ThemeProvider, withTheme } from 'styled-components'

const Box1 = styled.div`
color:${props => props.theme.color};
`
const Box2 = styled.div`
color:${props => props.theme.color};
`
const Box31 = styled.div`
color:${props => props.theme.color};
`

const Box3 = (props: any) => {
  console.log(props)
  return (<>
    <div>{props.children}</div>
    <Box31>box31</Box31>
  </>)
}

const Box4 = withTheme((props: any) => {
  console.log(props)
  return <div>{props.children}</div>
})

const Index = () => {
  return (
    <ThemeProvider theme={{ color: 'red' }}>
      <Box1>box1</Box1>
      <Box2>box2</Box2>
      <Box3>box3</Box3>
      <Box4>box4</Box4>
    </ThemeProvider>
  )
}

export default Index
复制代码

image.png

  • 样式组件通过“组件选择器”模式实现上下文覆盖,形成组合模式。
const BoxWrapper = styled.div`
  border:  1px solid #ccc;
  height: 100px;
  width: 100px;
  display: flex;
  justify-content: center;
  align-items: center;
  cursor: pointer;
`

const Box = styled.div`
  background: yellow;
  height: 50px;
  width: 50px;
  ${BoxWrapper}:hover & {
    background: red;
  }
`

const Index = () => {
  return (
    <BoxWrapper>
      <Box />
    </BoxWrapper>
  )
}
复制代码

好处

  1. 少量样式写在js里面,不必单独引入css文件,jsx+react+styled贯彻React的everything in js理念
  2. 使用js零学习成本,也不会多一道编译步骤,速度更快
  3. 样式可以使用变量,组件化写法
  4. 使用方便,不需要配置 webpack、开箱即用

不足

  1. 使用时感觉过度组件化
  2. 自动生成的类名可读性较差,样式过多时不好定位具体元素
文章分类
前端
文章标签