zoukankan      html  css  js  c++  java
  • react 样式冲突解决方案 styled-components

    前置

    在 react 中解决组件样式冲突的方案中,如果您喜欢将 css 与 js 分离,可能更习惯于 CSS-Modules;如果习惯了 Vue.js 那样的单文件组件,可能习惯于使用 styled-components 来解决这个问题。使用 CSS-Modules 从老项目迁移过来可能更容易。

    安装

    npm i styled-components
    

    基本用法

    import React from 'react'
    import { render } from 'react-dom'
    import styled from 'styled-components'
    
    const items = [
      {
        title: 'title1',
        type: 'primary',
        desc: 'Lorem ipsum dolor sit amet consectetur '
      },
      {
        title: 'title2',
        type: 'other',
        desc: 'Lorem ipsum dolor sit amet consectetur ',
      },
    ]
    
    function App() {
      return (
        <div>
          {items.map(renderItem)}
        </div>
      )
    }
    
    function renderItem(item) {
        return (
          <Wrap>
            <h2>{item.title}</h2>
            <p>{item.desc}</p>
          </Wrap>
        )
    }
    
    const Wrap = styled.div`
      margin: 10px auto 10px;
      padding: 10px;
       90%;
      border-radius: 5px;
      background: #eee
    `
    
    render(<App />, document.getElementById('app'))
    

    实际渲染结果:


    我们需要在 JavaScript 模板字符串内部书写 css 样式,为了得到 css 语法高亮,可以使用 vscode 扩展。

    嵌套

    const Wrap = styled.div`
      margin: 10px auto;
      padding: 10px;
       90%;
      border-radius: 5px;
      background: #eee;
    
    + h2 {
    +   color: red
    + }
    `
    

    props

    function renderItem(item) {
        return (
    +     <Wrap type={item.type} >
            <h2>{item.title}</h2>
            <p>{item.desc}</p>
          </Wrap>
        )
    }
    
    const Wrap = styled.div`
      margin: 10px auto;
      padding: 10px;
       90%;
      border-radius: 5px;
    + background: ${props => props.type === 'primary' ? '#202234' : '#eee'};
    `
    

    在模板括号中可使用任意 JavaScript 表达式,这里使用箭头函数,通过 props 接收参数。

    继承

    使用继承实现上文功能:

    import React from 'react'
    import { render } from 'react-dom'
    import styled from 'styled-components'
    
    const items = [...]
    
    function App() {
      return (...)
    }
    
    function renderItem(item) {
    + const Container = item.type === 'primary' ? primaryContainer : OrdinaryContainer
        return (
    +     <Container>
            <h2>{item.title}</h2>
            <p>{item.desc}</p>
          </Container>
        )
    }
    
    const Wrap = styled.div`
      margin: 10px auto 10px;
      padding: 10px;
       90%;
      border-radius: 5px;
    - background: ${props => props.type === 'primary' ? '#202234' : '#eee'};
    `
    
    + const OrdinaryContainer = styled(Wrap)`
    +   background: #eee;
    + `
    
    + const primaryContainer = styled(Wrap)` 
    +   background: #202234;
    + `
    
    render(<App />, document.getElementById('app'))
    

    我们得到同样的效果:

    继承的语法

    // 如上所示,您应该这样写来实现继承
    const OrdinaryContainer = styled(Wrap)``
    // 现在已经不支持extend关键字
    const OrdinaryContainer = Wrap.extend``
    

    下面给它们共同继承的 Wrap 添加一个border:

    const Wrap = styled.div`
      margin: 10px auto 10px;
      padding: 10px;
       90%;
      border-radius: 5px;
    + border: 2px solid red;
    `
    

    将影响所有继承过 Wrap 的样式变量:

    attrs

    封装一个文本输入框组件 src/components/Input.jsx

    import styled from "styled-components";
    
    const Input = styled.input.attrs({
        type: 'text',
        padding: props => props.size || "0.5em",
        margin: props => props.size || "0.5em"
    })`
     border: 2px solid #eee;
     color: #555;
     border-radius: 4px;
     margin: ${props=>props.margin};
     padding: ${props=>props.padding};
    `
    
    export default Input
    

    使用

    // ..
    import Input from './components/Input'
     
    function App() {
      return (
        <div>
          <Input></Input>
          <Input size="2em"></Input>
        </div>
      )
    }
    
    render(<App />, document.getElementById('app'))
    

    createGlobalStyle

    在 V4 版本已经将 injectGlobal 移除,使用 createGlobalStyle 代替它设置全局样式,

    import React from 'react'
    import { render } from 'react-dom'
    import styled, {createGlobalStyle} from 'styled-components'
    
    // ...
    
    function App() {
      return (
        <div>
          <GlobalStyle></GlobalStyle>
          {items.map(renderItem)}
        </div>
      )
    }
    
    function renderItem(item) {
      const Container = item.type === 'primary' ? primaryContainer : OrdinaryContainer
        return (
          <Container>
            <h2>{item.title}</h2>
            <p>{item.desc}</p>
          </Container>
        )
    }
    
    const GlobalStyle = createGlobalStyle`
      *{
        margin: 0;
        padding: 0;
       }
       body {
         min-height: 100%;
         background: #ffb3cc;
       }
    `
    // ...
    
    render(<App />, document.getElementById('app'))
    

    ThemeProvider

    通过上下文 API 将主题注入组件树中位于其下方任何位置的所有样式组件中。

    import React from 'react'
    import { render } from 'react-dom'
    import styled, {createGlobalStyle, ThemeProvider} from 'styled-components'
    import Input from './components/Input'
    
    const items = [...]
    
    function App() {
      return (
        <div>
          <GlobalStyle></GlobalStyle>
          {items.map(renderItem)}
          <Input></Input>
          <Input size="2em"></Input>
        </div>
      )
    }
    
    function renderItem(item) {
      const Container = item.type === 'primary' ? primaryContainer : OrdinaryContainer
        return (
          <Container>
            <h2>{item.title}</h2>
            <p>{item.desc}</p>
          </Container>
        )
    }
    
    // ...
    
    const primaryContainer = styled(Wrap)` 
      background: ${props=>props.theme.primary};
    `
    
    const theme = {
      primary: '#202234'
    }
    
    render(<ThemeProvider theme={theme}><App /></ThemeProvider>, document.getElementById('app'))
    

    我们需要先导入 ThemeProvider,然后用标签将 <App /> 包裹,需要注意的是必须为 ThemeProvider 标签提供一个 theme 属性,接下来在所有子组件中都可以通过 props.theme.xxx 获取 theme 下的属性。我们将 Input 组件的背景色同样改为 theme 下的 primary

    import styled from "styled-components";
    
    const Input = styled.input.attrs({
        // ...
    })`
     // ...
     background: ${props=>props.theme.primary};
    `
    
    export default Input
    

    keyframes

    使用 styled-components 时,无法直接在模板字符串中创建 keyframes,需要先导入 styled-components 下的 keframes 对象来创建它。下面看一个简单的实例:

    import React from 'react'
    import styled, { keyframes } from 'styled-components'
    import { render } from 'react-dom'
    
    const items = [...]
    
    function App() {
      return (
        <div>
          {items.map(renderItem)}
        </div>
      )
    }
    
    function renderItem(item) {
      const Container = item.type === 'primary' ? primaryContainer : OrdinaryContainer
        return (
          <Container>
            <h2>{item.title}</h2>
            <p>{item.desc}</p>
          </Container>
        )
    }
    
    // ...
    
    const fadeIn = keyframes`
      0% {
        opacity: 0;
      }
      100% {
        opacity: 1;
      }
    `
    
    const Wrap = styled.div`
      // ...
      animation: 1.5s ${fadeIn} ease-out;
    `
    // ..
    
    render(<App />, document.getElementById('app'))
    

    到我重新刷新页面,就能看见下面的效果:

  • 相关阅读:
    干货 | 日均流量200亿,携程高性能全异步网关实践 https://mp.weixin.qq.com/s/JdbPf_H4pn5PnPH2LIKQlw
    go获取机器的mac地址和ip
    扫清障碍和风险
    如何优雅地记录操作日志
    WebSocket 基础与应用系列(一
    pytest测试框架
    两个循环依赖的类中都有@Async 报错问题
    jvm 分析常用笔记
    Qt5.2~5.8下载地址
    windows的bat脚本中文乱码或者文件中文乱码
  • 原文地址:https://www.cnblogs.com/guangzan/p/13448225.html
Copyright © 2011-2022 走看看