如何在React中使用CSS样式(附代码示例)

590 阅读11分钟

在现代React中,有很多方法可以用CSS来设计React应用程序。每当我为有抱负的React开发者举办React研讨会时,由于时间有限,我只展示其中的一种方法,用于完整的React研讨会。但往往这一种造型方式并不足以涵盖这个重要话题的全部内容。有许多不同的策略(如CSS-in-JS)和这些策略中的许多不同方法(如风格化组件)需要学习:

  • CSS-in-CSS(例如:CSS、Sass、CSS模块或带Sass的CSS模块)
  • CSS-in-JS(例如样式化组件、Emotion)。
  • 实用至上的CSS(如Tailwind CSS)。

请跟随我的React之旅,了解更多关于CSS中这些不同的策略和方法,来设计你的React组件。对于所有不同的方式,我们将从相同的React组件开始:

import React from 'react';

function App() {
  const [fruits, setFruits] = React.useState([
    { id: '1', name: 'Apple', isFavorite: false },
    { id: '2', name: 'Peach', isFavorite: true },
    { id: '3', name: 'Strawberry', isFavorite: false },
  ]);

  function handleClick(item) {
    const newFruits = fruits.map((fruit) => {
      if (fruit.id === item.id) {
        return {
          id: fruit.id,
          name: fruit.name,
          isFavorite: !fruit.isFavorite,
        };
      } else {
        return fruit;
      }
    });

    setFruits(newFruits);
  }

  return (
    <div>
      <h3>with no styling</h3>

      <Basket items={fruits} onClick={handleClick} />
    </div>
  );
}

function Basket({ items, onClick }) {
  return (
    <ul>
      {items.map((item) => (
        <li key={item.id}>
          {item.name}
          <button type="button" onClick={() => onClick(item)}>
            {item.isFavorite ? 'Unlike' : 'Like'}
          </button>
        </li>
      ))}
    </ul>
  );
}

export default App;

这个小的React应用只是用一个有状态的列表来渲染一个列表组件。列表的每个项目都有一个按钮,帮助我们通过按钮及其回调处理程序来喜欢或不喜欢一个列表项目。在接下来的步骤中,我们将用不同的CSS样式设计按钮和列表。只要有CSS样式文件,我们就会在React项目中使用一个流行的文件夹结构

CSS-in-CSS:React中的CSS

最基本的方法是在React中使用普通的CSS与CSS文件。在每个组件或每组组件的旁边,你可以有一个扩展名为.css的文件。例如,下面这个CSS文件为一个按钮定义了一个CSS类。

.button {
  cursor: pointer;
  border: 1px solid #1a202c;
  padding: 8px;
  min-width: 64px;

  background: transparent;

  transition: all 0.1s ease-in;
}

.button:hover {
  background: #1a202c;
  color: #ffffff;
}

在React JavaScript文件中,我们可以从这个样式文件中导入CSS并隐含地使用它。

import React from 'react';
import './style.css';

function App() {
  ...
}

function Basket({ items, onClick }) {
  return (
    <ul>
      {items.map((item) => (
        <li key={item.id}>
          {item.name}
          <button
            type="button"
            className="button"
            onClick={() => onClick(item)}
          >
            {item.isFavorite ? 'Unlike' : 'Like'}
          </button>
        </li>
      ))}
    </ul>
  );
}

没有直接的连接--像一个变量--让我们在JSX中用className属性定义这个CSS类。相反,通过导入CSS文件,所有的CSS类都可以在这里使用。

让我们继续对列表进行样式设计。在CSS文件中,我们可以为列表和列表项再添加两个CSS类。

.unordered-list {
  margin: 0;
  padding: 0;
  list-style-type: none;
}

.list-item {
  display: flex;
  justify-content: space-between;
  padding: 8px 0;
}

然后我们又可以在React的JSX中用CSS className属性来使用它们。由于我们已经导入了CSS文件,我们可以直接使用CSS类。

function Basket({ items, onClick }) {
  return (
    <ul className="unordered-list">
      {items.map((item) => (
        <li key={item.id} className="list-item">
          {item.name}
          <button
            type="button"
            className="button"
            onClick={() => onClick(item)}
          >
            {item.isFavorite ? 'Unlike' : 'Like'}
          </button>
        </li>
      ))}
    </ul>
  );
}

这种CSS在React中的使用有几个缺点。首先,这只是普通的CSS,我们错过了很多高级的CSS功能。我们将用下一个叫做Sass的方法来改善这种情况,它与CSS-in-CSS策略相同。

CSS-in-CSS、React中的Sass

如果你使用create-react-app,你可以在安装后使用Sass。相反,如果你使用的是带有Webpack设置的自定义React,你需要为它配置Webpack。

Sass(Syntactically Awesome Style Sheets)是一个CSS扩展,为你提供更强大的CSS。例如,你可以定义可重复使用的CSS变量,并且你能够嵌套你的CSS。我们将利用后者来实现按钮的悬停效果。

.button {
  cursor: pointer;
  border: 1px solid #1a202c;
  padding: 8px;
  min-width: 64px;

  background: transparent;

  transition: all 0.1s ease-in;

  &:hover {
    background: #1a202c;
    color: #ffffff;
  }
}

在vanilla CSS中,我们必须定义另一个按钮的伪悬停类。在Sass中,我们可以使用父选择器&,指的是外部选择器(这里是.button )。这样,我们就可以将CSS选择器整齐地嵌套在一起,并从这些嵌套的选择器中引用父选择器。

新的CSS文件有一个Sass文件扩展名。将你的样式文件重命名为style.scss,并将其导入你的React JavaScript文件以进一步使用。

import React from 'react';
import './style.scss';

...

所有其他的样式和用法都和以前一样--当我们使用vanilla CSS时--因为我们在这里没有使用任何其他的Sass功能。请记住,当你使用CSS-in-CSS策略时,一定要选择像Sass这样的CSS扩展,以便在使用CSS时给自己更多的功能(嵌套CSS、变量和特殊选择器,如父选择器)。

在React中以这种方式使用CSS--即使是使用Sass--还有一个缺点。所有的CSS在导入后都可以全局访问。在React项目的其他地方,你可以重复使用按钮、列表和列表项的CSS类。有时这可能是你想要的效果,但大多数情况下,你想把你的样式/CSS范围扩大到一个JavaScript文件或一个React组件。让我们进入CSS模块 ...

CSS-in-CSS、React中的CSS模块

如果你使用create-react-app,你就可以马上使用CSS Modules。但是,如果你使用的是带有Webpack设置的自定义React,你需要为它配置Webpack。

CSS模块可以用于普通的CSS,也可以用于像Sass这样的CSS扩展。让我们看看如何在style.module.css(vanilla CSS)或style.module.scss文件(Sass)中定义一个CSS模块。

.button {
  cursor: pointer;
  border: 1px solid #1a202c;
  padding: 8px;
  min-width: 64px;

  background: transparent;

  transition: all 0.1s ease-in;
}

.button:hover {
  background: #1a202c;
  color: #ffffff;
}

如果你使用Sass与CSS模块,你可以再次使用所有的Sass功能,如&父选择器:

.button {
  cursor: pointer;
  border: 1px solid #1a202c;
  padding: 8px;
  min-width: 64px;

  background: transparent;

  transition: all 0.1s ease-in;

  &:hover {
    background: #1a202c;
    color: #ffffff;
  }
}

在你的React JavaScript文件中,你可以再次导入style.module.cssstyle.module.scss文件,但这次是用JavaScript样式对象明确导入:

import React from 'react';
import styles from './style.module.css';

...

如果你使用Sass,使用*.scss而不是.css*文件扩展名。这个新的JavaScript样式对象,无非是一个普通的JavaScript对象,持有你的CSS文件中的所有样式。你可以在你的React组件的JSX中使用它。

function Basket({ items, onClick }) {
  return (
    <ul>
      {items.map((item) => (
        <li key={item.id}>
          {item.name}
          <button
            type="button"
            className={styles.button}
            onClick={() => onClick(item)}
          >
            {item.isFavorite ? 'Unlike' : 'Like'}
          </button>
        </li>
      ))}
    </ul>
  );
}

CSS类可以作为导入的样式对象的属性。我们可以为列表和列表项类做同样的事情。首先在你的CSS文件中定义它们。

.unordered-list {
  margin: 0;
  padding: 0;
  list-style-type: none;
}

.list-item {
  display: flex;
  justify-content: space-between;
  padding: 8px 0;
}

由于之前按钮的使用,这两个CSS类已经被导入,我们可以直接在React的JSX中使用它们。

function Basket({ items, onClick }) {
  return (
    <ul className={styles['unordered-list']}>
      {items.map((item) => (
        <li key={item.id} className={styles['list-item']}>
          {item.name}
          <button
            type="button"
            className={styles.button}
            onClick={() => onClick(item)}
          >
            {item.isFavorite ? 'Unlike' : 'Like'}
          </button>
        </li>
      ))}
    </ul>
  );
}

CSS类通常以kebab大小写定义。如果是按钮样式,你可以用styles.button 。然而,对于其他带破折号的样式,你需要用字符串从对象中获取。

总之,如果你想使用CSS-in-CSS作为造型策略,CSS模块与Sass这样的扩展是现代React的现状。如果你想使用CSS-in-JS,你会选择像Styled Components这样的东西。

CSS-in-JS:React中的样式化组件

样式化组件需要进行CSS设置,因为一切都带有JavaScript。基本上,正如CSS-in-JS的策略已经说过的,我们将不需要任何CSS文件,因为所有的CSS都在JavaScript中定义。在使用风格化组件之前,你需要在命令行中安装它们。

npm install styled-components

Styled Components采取的方法是只用一个HTML标签和一个样式字符串来创建组件。让我们看看一个按钮元素在我们的JavaScript文件中是如何成为一个Button组件的。

import React from 'react';
import styled from 'styled-components';

const Button = styled.button`
  cursor: pointer;
  border: 1px solid #1a202c;
  padding: 8px;
  min-width: 64px;

  background: transparent;

  transition: all 0.1s ease-in;

  &:hover {
    background: #1a202c;
    color: #ffffff;
  }
`;

Button变量是一个有效的React组件,可以在JSX中使用。任何属性如onClick ,都会传递给真正的按钮HTML元素。此外,一个样式化组件已经有了一些功能(这里:带有父选择器的CSS嵌套),我们通常会从Sass这样的CSS扩展中获得。

function Basket({ items, onClick }) {
  return (
    <ul>
      {items.map((item) => (
        <li key={item.id}>
          {item.name}
          <Button type="button" onClick={() => onClick(item)}>
            {item.isFavorite ? 'Unlike' : 'Like'}
          </Button>
        </li>
      ))}
    </ul>
  );
}

对于许多React初学者来说,样式化组件的语法并不十分清楚。基本上,styled 对象为每个HTML元素(如按钮、ul、li)提供一个函数。这个函数可以用JavaScript模板字头调用,而你放在模板字头里的所有东西都会成为组件的样式。

const UnorderedList = styled.ul`
  margin: 0;
  padding: 0;
  list-style-type: none;
`;

const ListItem = styled.li`
  display: flex;
  justify-content: space-between;
  padding: 8px 0;
`;

风格化的组件可以定义在同一个文件或其他地方。毕竟,在你定义了它们之后,它们只是普通的React组件,这使得它们可以在你的JSX中导出或直接使用。

function Basket({ items, onClick }) {
  return (
    <UnorderedList>
      {items.map((item) => (
        <ListItem key={item.id}>
          {item.name}
          <Button type="button" onClick={() => onClick(item)}>
            {item.isFavorite ? 'Unlike' : 'Like'}
          </Button>
        </ListItem>
      ))}
    </UnorderedList>
  );
}

使用像Styled Components这样的CSS-in-JS方法,你仍然需要写CSS,但你用JavaScript来写。此外,像Styled Components这样的库已经解决了许多我们之前必须用CSS Modules(范围)和Sass(CSS特性)解决的问题。

实用为先--CSS、React中的Tailwind CSS

最后但并非最不重要的是,在CSS-in-CSS和CSS-in-JS策略的旁边,存在着Utility-First-CSS。实用优先CSS的方法之一是Tailwind CSS。让我们看看在你设置了它之后是什么样子的。请注意,Tailwind CSS在使用前需要进行一些适当的设置(在React中)。请查看Tailwind CSS官方网站的说明。之后,你可以为你的React组件导入Tailwind CSS。

import React from 'react';

import '../tailwind.generated.css';

...

当使用像Tailwind CSS这样的实用优先CSS策略时,你不需要再定义你的CSS。Tailwind CSS为你提供了所有预先配置好的CSS,你可以在你的React的classNames中立即使用。让我们来看看我们的按钮例子是怎样的。

function Basket({ items, onClick }) {
  return (
    <ul>
      {items.map((item) => (
        <li key={item.id}>
          {item.name}
          <button
            type="button"
            className="p-2 w-16 border border-solid border-gray-900 transition duration-100 ease-in hover:bg-gray-900 hover:text-white"
            onClick={() => onClick(item)}
          >
            {item.isFavorite ? 'Unlike' : 'Like'}
          </button>
        </li>
      ))}
    </ul>
  );
}

Tailwind CSS带有预配置的CSS类。例如,p-2 类为我们提供了0.5rem的所有方向的padding,如果没有其他配置的话,通常翻译为8px。也可以直接在你的JSX className属性中使用伪类的选择器(这里是hover)。

关于Tailwind CSS的坏处是,你不能再直接应用你的CSS知识,因为你必须学习他们的语法来表达所有的CSS属性,如宽度(这里是w-16)或颜色(border-gray-900)。然而,一旦你学会了Tailwind CSS的可用属性(或者至少知道如何浏览它们的文档),你可能会发现自己用CSS开发React组件比以往任何时候都快。与其从CSS中了解所有可能的键/值对,你几乎可以直接在你的JSX中使用该值。此外,Tailwind CSS带有很多合理的默认值,如颜色或填充/边距,这将自动导致一个更好看的应用程序。

让我们来看看我们如何用Tailwind CSS对列表和列表项元素进行样式设计:

function Basket({ items, onClick }) {
  return (
    <ul>
      {items.map((item) => (
        <li key={item.id} className="flex justify-between py-2">
          {item.name}
          <button
            type="button"
            className="p-2 w-16 border border-solid border-gray-900 transition duration-100 ease-in hover:bg-gray-900 hover:text-white"
            onClick={() => onClick(item)}
          >
            {item.isFavorite ? 'Unlike' : 'Like'}
          </button>
        </li>
      ))}
    </ul>
  );
}

列表项元素只是接收了用于其flexbox样式的CSS值,以及用于顶部和底部的垂直填充。列表本身没有接收任何CSS类,因为它已经在Tailwind CSS默认值中看起来很好了,它删除了CSS列表样式装饰和边距/填充。

Tailwind CSS对于那些愿意学习Tailwind CSS类的个人开发者或团队来说是非常好的,因为他们不需要再自己定义CSS。

React中的内联CSS

内联CSS(也叫内联样式)是上面的一个小奖励,因为它不应该取代任何其他显示的CSS方法。然而,有时了解它对快速原型设计或由JavaScript驱动的更多动态CSS是很有用的。例如,每个HTML元素都有一个style属性。你可以在React的JSX中使用style属性,向其传递一个style对象。

function Basket({ items, onClick }) {
  return (
    <ul>
      {items.map((item) => (
        <li key={item.id}>
          {item.name}
          <button
            type="button"
            onClick={() => onClick(item)}
            style={{
              cursor: 'pointer',
              border: '1px solid #1a202c',
              padding: '8px',
              minWidth: '64px',

              background: 'transparent',

              transition: 'all 0.1s ease-in',
            }}
          >
            {item.isFavorite ? 'Unlike' : 'Like'}
          </button>
        </li>
      ))}
    </ul>
  );
}

我们不需要定义任何其他的样式组件或CSS文件,因为我们可以直接将所有的样式作为对象传递给JSX的HTML元素。这同样适用于列表和列表项元素。

function Basket({ items, onClick }) {
  return (
    <ul
      style={{
        margin: '0',
        padding: '0',
        listStyleType: 'none',
      }}
    >
      {items.map((item) => (
        <li
          key={item.id}
          style={{
            display: 'flex',
            justifyContent: 'space-between',
            padding: '8px 0',
          }}
        >
          {item.name}
          <button
            type="button"
            onClick={() => onClick(item)}
            style={{
              cursor: 'pointer',
              border: '1px solid #1a202c',
              padding: '8px',
              minWidth: '64px',

              background: 'transparent',

              transition: 'all 0.1s ease-in',
            }}
          >
            {item.isFavorite ? 'Unlike' : 'Like'}
          </button>
        </li>
      ))}
    </ul>
  );
}

你已经可以看到这种方法的负面影响:你的JSX变得不可读,因为所有的样式都在你的HTML标签中杂乱不堪。这就是为什么你在常规的React项目中很少看到内联样式。然而,如前所述,它可以在原型设计或基于JavaScript条件的动态CSS中派上用场。


毕竟,个人的品味和特点会影响你和你的团队的React项目采取哪种样式策略和方法的决定。在现代React应用中,你会发现每一种策略都有最流行的方法。CSS模块、风格化组件和Tailwind CSS。你可以在这个GitHub资源库中找到造型策略中的所有不同方法。