美文网首页
React中的样式

React中的样式

作者: 张_何 | 来源:发表于2022-05-17 13:32 被阅读0次
    • 目前整个前端已经是组件化的天下:
    • 而CSS的设计就不是为组件化而生的,所以在目前组件化的框架中都在需要一种合适的CSS解决方案。
    • 在组件化中选择合适的CSS解决方案应该符合以下条件:
    • 可以编写局部css:css具备自己的局部作用域,不会随意污染其他组件内的元素;
    • 可以编写动态的css:可以获取当前组件的一些状态,根据状态的变化生成不同的css样式;
    • 支持所有的css特性:伪类、动画、媒体查询等;
    • 编写起来简洁方便、最好符合一贯的css风格特点;
    • 等等...
    • 事实上,css一直是React的痛点,也是被很多开发者吐槽、诟病的一个点。
    • 在这一点上,Vue做的要确实要好于React:
    • Vue通过在.vue文件中编写 <style><style> 标签来编写自己的样式;
    • 通过是否添加 scoped 属性来决定编写的样式是全局有效还是局部有效;
    • 通过 lang 属性来设置你喜欢的 less、sass等预处理器;
    • 通过内联样式风格的方式来根据最新状态设置和改变css;
    • 等等...
    • Vue在CSS上虽然不能称之为完美,但是已经足够简洁、自然、方便了,至少统一的样式风格不会出现多个开发人员、多个项目
      采用不一样的样式风格。
    • 相比而言,React官方并没有给出在React中统一的样式风格:
    • 由此,从普通的css,到css modules,再到css in js,有几十种不同的解决方案,上百个不同的库;
    • 大家一致在寻找最好的或者说最适合自己的CSS方案,但是到目前为止也没有统一的方案;
    内联样式
    • 内联样式是官方推荐的一种css样式的写法:
    • style 接受一个采用小驼峰(原生中使用的是连接符)命名属性的 JavaScript 对象<h2 style={{fontSize: "50px", color: "red"}}>我是标题</h2>,而不是 CSS 字符串;
    • 并且可以引用state中的状态来设置相关的样式;
    • style 中属性值需要用双引号或单引号引起来, 不然会被认为是变量
      constructor(props) {
        super(props);
        this.state = {
          color: "purple"
        }
      }
      render() {
        const pStyle = {
          color: this.state.color,
          textDecoration: "underline"
        }
        return (
          <div>
            <h2 style={{fontSize: "50px", color: "red"}}>我是标题</h2>
            <p style={pStyle}>我是一段文字描述</p>
          </div>
        )
      }
    
    • 内联样式的优点:
    • 1.内联样式, 样式之间不会有冲突
    • 2.可以动态获取当前state中的状态
    • 内联样式的缺点:
    • 1.写法上都需要使用驼峰标识
    • 2.某些样式没有提示,比如在标签内写 style 属性时有提示,但是如果像上面 pStyle 拿到外面写就不会有提示了
    • 3.大量的样式, 代码混乱
    • 4.某些样式无法编写(比如伪类/伪元素)
    • 所以官方依然是希望内联合适和普通的css来结合编写;
    普通的 CSS
    • 普通的css我们通常会编写到一个单独的文件,之后再进行引入。
    • 这样的编写方式和普通的网页开发中编写方式是一致的:
    • 如果我们按照普通的网页标准去编写,那么也不会有太大的问题;
    • 但是组件化开发中我们总是希望组件是一个独立的模块,即便是样式也只是在自己内部生效,不会相互影响;
    • 但是普通的css都属于全局的css,样式之间会相互影响;
    • 这种编写方式最大的问题是样式之间会相互层叠掉;
      比如我们在 home 组件中有个 css 文件,写了个title 选择器的样式
    .title {
      font-size: 30px;
      color: red;
    }
    

    然后在 profile 组件中有个 css 文件,也写了个 title 选择器的样式

    .title {
      color: yellow;
    }
    

    由于 css 是全局的, 所以这两个 title 选择器就会发生层叠,也就是在使用到 title 选择器的地方要么都显示红色,要么都显示黄色, 不会是我们希望的 home 中显示红色,profile 中显示黄色

    css modules
    • css modules并不是React特有的解决方案,而是所有使用了类似于webpack配置的环境下都可以使用的。
    • 但是,如果在其他项目中使用个,那么我们需要自己来进行配置,比如配置webpack.config.js中的modules: true等。
    • React的脚手架已经内置了css modules的配置:
    • .css/.less/.scss 等样式文件都修改成.module.css/.module.less/.module.scss 等;
    • 之后就可以引用并且进行使用了;
      比如我们定义了一个 style.module.css 文件,文件内容如下:
    .title {
      font-size: 30px;
      color: red;
    }
    .banner {
      color: orange;
    }
    

    在 index.js 文件中的使用如下:

    import React, { PureComponent } from 'react';
    import homeStyle from './style.module.css'; // 这里我们按包导入的方式导入,在使用的时候都是用homeStyle.xxx 的方式使用
    
    export default class Home extends PureComponent {
      render() {
        return (
          <div className="home">
            <h2 className={homeStyle.title}>标题</h2>
            <div className={homeStyle.banner}>
              <span>轮播图</span>
            </div>
          </div>
        )
      }
    }
    
    • css modules确实解决了局部作用域的问题,也是很多人喜欢在React中使用的一种方案。
    • 但是这种方案也有自己的缺陷:
    • 引用的类名,不能使用连接符(.home-title),在JavaScript中是不识别的;
    • 所有的className都必须使用{style.className} 的形式来编写;
    • 不方便动态来修改某些样式,依然需要使用内联样式的方式;
    • 如果你觉得上面的缺陷还算OK,那么你在开发中完全可以选择使用css modules来编写,并且也是在React中很受欢迎的一种方式。
    CSS in JS
    • CSS-in-JS是指一种模式,其中 CSS 由 JavaScript 生成而不是在外部文件中定义。注意此功能并不是 React 的一部分,而是由第三方库提供。 React 对样式如何定义并没有明确态度。
    • 在传统的前端开发中,我们通常会将结构(HTML)、样式(CSS)、逻辑(JavaScript)进行分离。
    • React的思想中认为逻辑本身和UI是无法分离的,所以才会有了JSX的语法。
    • 样式也是属于UI的一部分;
    • 事实上CSS-in-JS的模式就是一种将样式(CSS)也写入到JavaScript中的方式,并且可以方便的使用JavaScript的状态;
    • 所以React又被人称之为 All in JS;
    • 当然,这种开发的方式也受到了很多的批评:
    • 批评声音虽然有,但是在我们看来很多优秀的CSS-in-JS的库依然非常强大、方便:
    • CSS-in-JS通过JavaScript来为CSS赋予一些能力,包括类似于CSS预处理器一样的样式嵌套、函数定义、逻辑复用、动态修
      改状态等等;
    • 虽然CSS预处理器也具备某些能力,但是获取动态状态依然是一个不好处理的点;
    • 所以,目前可以说CSS-in-JS是React编写CSS最为受欢迎的一种解决方案;
    • 目前比较流行的 CSS-in-JS 的库有 styled-componentsemotion , 前者是社区最流行的 CSS-in-JS库
    styled-components
    • 安装命令: yarn add styled-components
    • 要学习styled-components首先要了解 ES6 的标签模板字符串,详见JavaScript中的 ES6标签模板字符串;
    • 在styled component中,就是通过这种方式来解析模块字符串,最终生成我们想要的样式的
    • styled-components的本质是通过函数的调用,最终创建出一个组件:
    • 这个组件会被自动添加上一个不重复的class;
    import React, { Component } from 'react';
    import styled from 'styled-components'
    const DivCmp = styled.div`
      color:red;
    `
    export default class App extends Component {
      render() {
        return (
          <DivCmp>
            <h2>用户昵称</h2>
          </DivCmp>
        )
      }
    }
    

    如上图 styled.div`...` 会给我返回一个 div 类型的标签,styeld 后面跟什么就会返回什么类型,如果是 h1, 返回的就是h1,如果是 span,就返回 span, 如果没有这种类型会不显示,

    • styled-components会给该class添加相关的样式;如上图生成一个给 div 添加 class="sc-hKMtZM gt0Tnf" 的样式,这个字符串是唯一的,不会与别的重复
    • props 属性穿透, props 可以被传递给 styled 组件,获取 props 需要通过${}传入一个插值函数,props 会作为该函数的参数,这种方式可以有效解决动态样式的问题
    import React, { Component } from 'react';
    import styled from 'styled-components'
    const DivCmp = styled.div`
      color:red;
      font-size: ${props=>props.fontSize};
      text-decoration: ${props=>props.textDecoration};
    `
    export default class App extends Component {
      render() {
        return (
          <DivCmp fontSize="55px" textDecoration="underline">
            <span>用户昵称</span>
          </DivCmp>
        )
      }
    }
    
    • attrs 函数, 通过styled-components创建出来的 styled 组件都会有一个attrs函数, 该函数接收一个对象作为参数, 并返回 styled 组件。
    • 参数中我们既可以放置组件的属性,也能放置组件的样式
    import React, { Component } from 'react';
    import styled from 'styled-components'
    
    const HYInput = styled.input.attrs({
      placeholder: "请输入文字", // 这里和HYInput组件中设置placeholder属性是一样的, 
      bColor: "red" // 这里和在HYInput组件中设置一个 bColor 属性是一样的,可以通过 props 来取
    })`
      background-color: lightblue;
      border-color: ${props => props.bColor}; // 取出 attrs 中的 bColor
      color: ${props => props.color};// 取出通过属性传过来的 color
    `
    export default class App extends Component {
      constructor(props) {
        super(props);
        this.state = {
          color: "purple"
        }
      }
      render() {
        return (
          <div>
            {/* 从 state 中取出 color 赋值给 color 属性, 这样就能动态改变样式了 */}
            <HYInput type="password" color={this.state.color}/>
          </div>
        )
      }
    }
    
    • 继承, styled组件支持继承, 当很多组件有同样的样式时,我们就可以将同样的样式抽取成一个基类,然后不一样的样式部分再实现子类,通过子类继承基类来达到样式的服用。下面是继承的例子子组件通过styled(父组件名)的方式来继承
    import React, { PureComponent } from 'react';
    import styled from 'styled-components';
    
    const HYButton = styled.button`
      padding: 10px 20px;
      border-color: red;
      color: red;
    `
    
    const HYPrimaryButton = styled(HYButton)`
      color: #fff;
      background-color: green;
    `
    
    export default class App extends PureComponent {
      render() {
        return (
            <HYButton>我是普通的按钮</HYButton>
            <HYPrimaryButton>我是主要的按钮</HYPrimaryButton>
        )
      }
    }
    
    • styled 提供了 ThemeProvider 来共享主题
    import React, { PureComponent } from 'react';
    import styled, { ThemeProvider } from 'styled-components';
    
    const TitleWrapper = styled.h2`
      text-decoration: underline;
      color: ${props => props.theme.themeColor};
      font-size: ${props => props.theme.fontSize};
      `
    
    class Home extends PureComponent {
      render() {
        return (
          <TitleWrapper>我是home的标题</TitleWrapper>
        )
      }
    }
    
    export default class App extends PureComponent {
      render() {
        return (
          <ThemeProvider theme={{themeColor: "red", fontSize: "25px"}}>
            <Home />
          </ThemeProvider>
        )
      }
    }
    
    React 中动态修改class
    • React在JSX给了我们开发者足够多的灵活性,你可以像编写JavaScript代码一样,通过一些逻辑来决定是否添加某些class:
    <div>
      <h2 className={"title " + (isActive ? "active" : "")}> 我是标题</h2>
      <h2 className={["title ", (isActive ? "active" : "")].join(" ")}> 我是标题</h2>
    </div>
    
    • 我们也可以借助第三方库: classnames, 这是一个用于动态添加 className 的库
    classNames('foo','bar'); // 'foo bar'
    classNames('foo',{bar: true}); // 'foo bar'
    classNames({'foo-bar': true}); // 'foo bar'
    classNames({'foo-bar': false}); // ''
    classNames({foo: true},{bar: true}); // 'foo bar'
    classNames({foo: true, bar: true}); // 'foo bar'
    classNames('foo', {bar: true, duck: false}, 'baz', {quux: true}); // 'foo bar baz quux'
    classNames(null,false,'bar',undefined, 0,1,{baz: null},''); // 'bar 1'
    

    相关文章

      网友评论

          本文标题:React中的样式

          本文链接:https://www.haomeiwen.com/subject/wysmurtx.html