React css样式隔离

4,775 阅读3分钟

一、Vue 项目中的样式隔离

Vue通过在.vue文件中编写 <style><style> 标签来编写自己的样式,通过是否添加 scoped 属性来决定编写的样式是全局有效还是局部有效

二、React 项目中的样式隔离

React官方并没有给出在React中样式隔离的方案:由此,从css modules,到css in js,有几十种不同的解决方案,上百个不同的库,来帮助解决React防止样式全局污染

2-1 css modules

css modules 并不是React特有的解决方案,而是所有使用了类似于webpack配置的环境下都可以使用的。但是,如果在其他项目中使用个,那么我们需要自己来进行配置,比如配置 webpack.config.js 中的 modules: true等。

React的脚手架已经内置了css modules的配置,只需要将 .css/.less/.scss 样式文件名称都修改成 .module.css/.module.less/.module.scss 即可,css modules确实解决了局部作用域的问题,但是这种方案也有自己的缺陷:

  1. 引用的类名,不能使用连接符(.home-title),在JavaScript中是不识别的

  2. 所有的className都必须使用{style.className} 的形式来编写

  3. 不方便动态来修改某些样式,依然需要使用内联样式的方式

2-2 css in js

css in js 是指一种模式,其中 css 由 js 生成而不是在外部文件中定义,此功能并不是 React 的一部分,而是由第三方库提供。在传统的前端开发中,通常会将结构(html)、样式(css)、逻辑(js)进行分离。但是 React 的思想中认为逻辑本身和UI是无法分离的,所以才会有了JSX的语法。样式也是属于UI的一部分,事实上 css in js 的模式就是一种将样式(css)也写入到 js 中的方式,并且可以方便的使用 js 的状态,所以React有被人称之为 All in JS;

2-3 styled-components

  • styled-components 是一种 css in js 的方案,或者说是它一个比较流行的 css in js 库

  • styled-components 是利用了 ES6 中标签模板字符串的方法来调用函数的,并且通过这种方式来解析模块字符串,最终生成我们想要的样式的

// 模版字符串基本使用
const name = 'hzy'
const message = `my name is ${name}`

// 标签模版字符串:可以通过模版字符串的方式对一个函数进行调用
// ...arg 表示“可变参数”,arg 最终是一个数组,表示函数test可以接受多个参数
function test(...arg) {
    console.log(arg)
}
test('aaaa') // 打印 ['aaaa']
test`aaaa` // 打印 [0:['aaaa']] 是一个二维数组 
test`aaaa bbbb ${name} aaaa` // 打印 [0:["aaaa bbbb","aaaa"],1:"hzy"]
// 总结:会对差值语法进行切割,字符串会放到数组中,变量会单独拿出来放
  • styled-components 的本质是通过函数的调用,最终创建出一个组件:这个组件会被自动添加上一个不重复的className,styled-components会给该class添加相关的样式
import React, { PureComponent } from "react";
import styled from "styled-components";

// 本质还是执行函数的过程,这里的 button 相当于一个函数,返回一个 react 的组件
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 (
        <div>
            <HYButton>我是普通的按钮</HYButton>
            <HYPrimaryButton>我是主要的按钮</HYPrimaryButton>
        </div>
    );
  }
}
  • styled-components 可以将 props 传递给styled组件,获取 props 需要通过 ${} 传入一个插值函数,props 会作为该函数的参数,这种方式可以有效的解决动态样式的问题;还以通过 attrs 函数编写组件的属性
import React, { PureComponent } from "react";
import styled from "styled-components";

/**
 * 特点:
 *  1.props穿透,属性是会被 styled 拿到的
 *  2.attrs函数的使用,attrs函数本身会返回一个新的函数,attrs可以写组件的属性,这样就不用在标签上面写了
 *  3.传入state作为props属性
 */

const HYInput = styled.input.attrs({
  placeholder: "coderwhy",
  bColor: "red",
})`
  background-color: lightblue;
  border-color: ${(props) => props.bColor};
  color: ${(props) => props.color};
`;

export default class Profile extends PureComponent {
  constructor(props) {
    super(props);

    this.state = {
      color: "purple",
    };
  }

  render() {
    return (
      <div>
        <input type="password" />
        <HYInput type="password" color={this.state.color} />
        <h2>我是Profile的标题</h2>
        <ul>
          <li>设置列表1</li>
          <li>设置列表2</li>
          <li>设置列表3</li>
        </ul>
      </div>
    );
  }
}

  • styled-components 还可以通过 ThemeProvider 来设置主题

app/index.js

import React, { PureComponent } from "react";
import Home from "../home";
import styled, { ThemeProvider } from "styled-components";
// ThemeProvider 主要是用于设置主题


export default class App extends PureComponent {
  render() {
    return (
      <ThemeProvider theme={{ themeColor: "red", fontSize: "30px" }}>
        {/* 这里的 theme 会通过 props 传入所有子元素,所有子元素通过 props 都可以拿到 theme*/}
        <Home />
      </ThemeProvider>
    );
  }
}

home/index.js

import React, { PureComponent } from 'react';

import { 
  HomeWrapper,
  TitleWrapper
} from "./style";

export default class Home extends PureComponent {
  render() {
    return (
      <HomeWrapper>
        <TitleWrapper>我是home的标题</TitleWrapper>
        <div className="banner">
          <span>轮播图1</span>
          <span className="active">轮播图2</span>
          <span>轮播图3</span>
          <span>轮播图4</span>
        </div>
      </HomeWrapper>
    )
  }
}

home/style.js

import styled from "styled-components";

export const HomeWrapper = styled.div`
  font-size: 12px;
  color: red;

  .banner {
    background-color: blue;

    span {
      color: #fff;

      /* 这里的 & 表示 span 的元素 */
      &.active {
        color: red;
      }

      &:hover {
        color: green;
      }

      &::after {
        content: "aaa";
      }
    }

    /* .active {
      color: #f00;
    } */
  }
`;

export const TitleWrapper = styled.h2`
  text-decoration: underline;
  color: ${(props) => props.theme.themeColor}; // 获取主题 theme 共享的数据
  font-size: ${(props) => props.theme.fontSize};
`;