前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >如何在 React 中高效管理 CSS 类

如何在 React 中高效管理 CSS 类

作者头像
摸五休二
修改2024-06-18 09:21:04
1000
修改2024-06-18 09:21:04

在使用 React 构建应用程序时,我们通常希望组件能够根据用户交互动态改变其外观。通过使用条件样式类(conditional CSS classes),可以轻松实现这些变化,这些类根据特定条件进行应用或移除。

在 React 中,这些类通常根据组件的 prop 值或状态进行应用。三元运算符经常用于管理这些类的应用。下面的代码片段展示了这种常见方法的示例:

代码语言:javascript
复制
import styles from "./button.module.css"

function Button({variant}){
  return (
    <button className={`${styles.base} ${variant ? styles[variant] : ""}`}> 
       {children}    
    </button>
  )
}

export default Button;

这种方法对于构建小型组件是典型的。然而,随着组件变得更加灵活,并且引入更多的 props 来处理这种灵活性,跟踪可用的 CSS 类及其应用条件变得困难。按钮组件可能最终会变成这样:

代码语言:javascript
复制
import styles from "./button.module.css"

function Button({variant, type, size}){  
  return (    
    <button className={`${styles.base} ${variant ? styles[variant] : ""} ${type ? styles[type] : ""} ${size ? styles[size] : ""}`}>      
      {children}    
    </button>  
  )
}

export default Button

条件样式类在元素上的应用逻辑长度使得很难理解这些 CSS 类是如何被应用到元素上的。这种困难可能会使得调试代码变得具有挑战性。高效地应用 CSS 类不仅对你未来的自己很重要,对于其他可能会参与该项目的开发者同样重要。

本文将探讨在 React 应用程序中管理条件样式类的高效技术。

前提条件

为了充分利用本文内容,您需要:

  • 具备 React 的基本知识
  • 熟悉 CSS 模块
  • 熟悉 ES6 语法
  • 安装了 Node.js

项目设置

我们将构建一个按钮组件,具有以下 props:

  • variant: solid, outlined, 和 text
  • type: primary, success, 和 danger
  • size: sm (小), md (中), 和 lg (大)

要跟随本文一起操作,您需要创建一个新的 React 应用程序。您可以在终端中执行以下命令来完成此操作:

代码语言:javascript
复制
npm create vite@latest <project_name> -- --template react

项目创建完成后,切换到您的项目目录,并执行以下命令以安装项目所需的依赖项:

代码语言:javascript
复制
npm install

安装必要的依赖项后,让我们对新的 React 应用程序进行一些更改。首先,删除 App.css 文件。我们不需要它,因为我们将使用 CSS 模块来为按钮组件设置样式。

接下来,在 src 目录内创建一个新的 components 目录。然后,在 components 目录中创建两个新文件:Button.jsx 和 button.module.css。

将以下 CSS 样式复制并粘贴到 button.module.css 文件中:

代码语言:javascript
复制
/* button base style */
.base {
  color: #fff;
  font-weight: bold;
  font-size: 0.75rem;
  padding: 4px;
  cursor: pointer;
  border-radius: 6px;
}

/*Button variant styles */
.solid,
.text {
  border: none;
}

.outline,
.text {
  background-color: transparent;
  color: rgb(133, 133, 133);
}

.outline {
  border: 2px solid rgb(133, 133, 133);
}

/* button types style */
.primary {
  background-color: #3b82f6;
}

.success {
  background-color: #22c55e;
}

.danger {
  background-color: #ef4444;
}

/* Compound button styles */
.solid-primary {
  background-color: #3b82f6;
}

.text-primary,
.outline-primary {
  background-color: transparent;
  color: #3b82f6;
}

.outline-primary {
  border: 2px solid #3b82f6;
}

.solid-success {
  background-color: #22c55e;
}

.text-success,
.outline-success {
  background-color: transparent;
  color: #22c55e;
}

.outline-success {
  border: 2px solid #22c55e;
}

.solid-danger {
  background-color: #ef4444;
}

.text-danger,
.outline-danger {
  background-color: transparent;
  color: #ef4444;
}

.outline-danger {
  border: 2px solid #ef4444;
}

/* button size styles */
.sm {
  font-size: 0.875rem;
  padding: 6px;
}

.md {
  font-size: 1rem;
  padding: 8px;
}

.lg {
  font-size: 1.125rem;
  padding: 10px;
}

接下来,用以下内容替换 index.css 文件中的 CSS 样式:

代码语言:javascript
复制
* {  
  padding: 0;  
  margin: 0;  
  box-sizing: border-box;
}

body {    
  min-height: 100vh;  
  display: grid;  
  place-items: center;
}

接下来,将以下代码复制并粘贴到 Button.jsx 文件中:

代码语言:javascript
复制
import styles from "./button.module.css"

function Button(){  
  return <button className={styles.base}>Button</button>  
}

export default Button;

接下来,用以下内容替换 App.jsx 文件中的内容:

代码语言:javascript
复制
import Button from "./components/Button";

function App() {
  return <Button />;
}

export default App;

最后,保存所有更改并执行以下命令以启动开发服务器:

代码语言:javascript
复制
npm run dev

您应该会在屏幕上看到如下按钮:

A light gray, slightly rounded rectangular button with the text 'button' in white, displaying only the base styles.
A light gray, slightly rounded rectangular button with the text 'button' in white, displaying only the base styles.

项目设置完成后,让我们来看一下在 React 中高效管理条件样式类应用的不同方法。

方法一:手动方法

手动方法涉及创建一个 CSS 类数组,然后使用 Array.join() 方法将这些类连接成一个字符串,该字符串将应用于组件。将此方法实现到我们的按钮组件中:

代码语言:javascript
复制
import styles from "./button.module.css";

// Set the default values for some props
function Button({ variant = "solid", size = "md", type, children }) {
  // Create an array of class names based on props
  const classNames = [
    styles.base, // Base class
    styles[size], // Size-specific class
    styles[variant], // Variant-specific class
    styles[`${variant}-${type}`], // Variant and type-specific class
  ];

  // Concatenate the classes into a string
  const btnStyles = classNames.join(" ");

  return <button className={btnStyles}>{children}</button>;
}

在上面的代码片段中,我们创建了一个包含所有用于按钮样式的 CSS 类的 classNames 数组。然后,我们使用 join() 方法将数组元素连接成一个字符串。

我们使用 join() 方法而不是 toString() 方法,因为 toString() 方法返回的字符串使用逗号作为分隔符来连接数组中的 CSS 类。当应用于元素时,这无法生成预期的样式。例如,使用 toString() 方法:

代码语言:javascript
复制
import styles from "./button.module.css";  

// Set the default values for some props
function Button({ variant = "solid", size = "md", type, children }) {  
  
  // Create an array of class names based on props  
  const classNames = [ 
    styles.base,  // Base class    
    styles[size],  // Size-specific class    
    styles[variant],  // Variant-specific class    
    styles[`${variant}-${type}`] // Variant and type-specific class
  ];
  
  // Using the toString method to Concatenate the classes into a string  
  const btnStyles = classNames.toString();
  
  return <button className={btnStyles}>{children}</button>;
}

保存更改后,我们得到这样的按钮:

A HTML button element with default styling and displaying the text button.
A HTML button element with default styling and displaying the text button.

当我们在浏览器的开发者工具中检查该元素时:

A devtools snapshot showing a button element with a single class that includes commas: "base_qizaw_2,sm_qizaw_84,solid_qizaw_13" due to JavaScript's toString method.
A devtools snapshot showing a button element with a single class that includes commas: "base_qizaw_2,sm_qizaw_84,solid_qizaw_13" due to JavaScript's toString method.

这些类被逗号分隔,并作为单个类而不是单独的类应用于按钮上。使用 join() 方法时,我们可以传递一个分隔符作为参数,在这种情况下,当我们调用 join() 方法时,使用空格作为分隔符。

当我们还原更改并保存文件后,在浏览器中我们会得到一个漂亮的按钮:

undefined(

https://res.cloudinary.com/dz209s6jk/image/upload/v1705575169/Admin/jwpu2fp5t1vxdejyonv8.jpg)

手动方法的优点

  • 简单实现:手动方法使用基本的 JavaScript,使其实现简单,因为它不需要学习曲线。
  • 提高代码清晰度:与使用三元运算符的内联方法相比,它更容易理解代码流程,使调试稍微容易一些。
  • 没有外部依赖:它不依赖外部库,从而减少了项目依赖项。

手动方法的缺点

  • 代码冗长:在处理更复杂的样式场景或更大的项目时,手动方法可能变得不太可维护。随着条件和样式数量的增加,代码可能变得复杂难以管理。
  • 没有明确的条件:很难理解在什么条件下将不同的 CSS 类应用于元素,这可能会使调试代码变得困难。

方法二:使用 clsx 库

clsx 是一个轻量级的实用库,用于管理 CSS 类的应用。它是一个简单的函数,接受对象、数组或字符串作为参数,并根据提供的条件返回有效类的字符串插值。

在终端中执行以下命令以安装 clsx 库:

代码语言:javascript
复制
npm install clsx

安装 clsx 库后,让我们重构我们的 Button 组件:

代码语言:javascript
复制
import clsx from "clsx";
import styles from "./button.module.css";

// Setting some default prop values
function Button({ variant = "solid", size = "md", type, children }) {
  const btnStyles = clsx({
    // Define class names as object properties
    // The base class for the button is always included
    [styles.base]: true,
    // Include the variant-specific class if the 'variant' prop is provided 
    [styles[variant]]: variant,
    // Include the size-specific class if the 'size' prop is provided
    [styles[size]]: size,
    // Include a compound class if the 'type' prop is provided
    [styles[`${variant}-${type}`]]: type,
  });

  return <button className={btnStyles}>{children}</button>;
}

export default Button;

在上面的代码片段中,我们调用了 clsx 函数并将其返回值存储在 className 变量中。我们向函数提供了一个对象作为参数,其中每个键表示一个根据其关联值有条件应用的 CSS 类。第一个类设置为 true,确保每次渲染按钮组件时都会应用该类。后续的键映射到不同的 props,并且只有在组件渲染时传递相应的 prop 值时才会应用这些类。

保存文件后,您将得到同样漂亮的按钮:

The primary button, a blue rectangular button with rounded corners and the text 'button' in white.
The primary button, a blue rectangular button with rounded corners and the text 'button' in white.

这种方法可以进一步优化,在应用相应的 CSS 类之前检查 prop 是否具有有效值,而不是在 prop 的值为 true 时应用与任何 prop 相关联的 CSS 类。这有助于避免由于向组件的任何 prop 传递无效值而导致应用未定义类的情况。要优化此方法,我们可以按如下方式进行:

代码语言:javascript
复制
import clsx from "clsx";
import styles from "./button.module.css";

function Button({ variant = "solid", size = "md", type, children }) {
  //Added an array of all the valid prop values
  const validVariants = ["solid", "outlined", "text"];
  const validTypes = ["primary", "success", "danger"];
  const validSizes = ["sm", "md", "lg"];

  const btnStyles = clsx({
    [styles.base]: true,
    // The classes are only applied when the prop has a true value
    // and the value is a valid option for the given prop
    [styles[variant]]: variant && validVariants.includes(variant),
    [styles[size]]: type && validSizes.includes(size),
    [styles[`${variant}-${type}`]]: type && validTypes.includes(type),
  });

  return <button className={btnStyles}>{children}</button>;
}

export default Button;

在上面的代码片段中,我们为每个 prop 创建了一个有效值数组。然后,我们使用 && 运算符确保只有在 prop 具有 true 值并且是特定 prop 的有效选项时,才包含与该 prop 关联的 CSS 类。这有助于防止应用未定义的 CSS 类。

使用 clsx 库方法的优点

  • 简洁代码:clsx 消除了冗长的内联条件语句的需要,使代码更加紧凑且易于理解。
  • 提高代码清晰度:clsx 通过明确定义每个类应用于元素的条件,提高了代码的清晰度,使其比手动方法更容易理解和维护。
  • 更易于维护:明确设置应用不同 CSS 类的条件,使代码更容易理解,因此更易于维护。

使用 clsx 库方法的缺点

  • 额外依赖:虽然 clsx 是一个小包(255B),但它仍然是项目的一个额外依赖项,这需要考虑。
  • 学习曲线:尽管不陡峭,但仍需要一些时间来有效使用 clsx 库。

方法三:使用 class-variance-authority 库

class-variance-authority(cva)是另一个用于管理组件中 CSS 类条件应用的实用库。cva 和 clsx 之间的关键区别在于,需要在 cva 中显式指定在渲染组件时根据不同 props 值的存在和组合应用于组件的样式。

在终端中执行以下命令以安装 cva 库:

代码语言:javascript
复制
npm install class-variance-authority 

重构 Button 组件后,我们现在有:

代码语言:javascript
复制
import { cva } from "class-variance-authority";
import styles from "./button.module.css";

function Button({ variant, type, size, children }) {  
  const btnStyles = cva([styles.base], {    
    variants: {      
      variant: {        
        solid: styles.solid,        
        outline: styles.outline,        
        text: styles.text,      
      },      
      type: {        
        primary: styles.primary,        
        success: styles.success,        
        danger: styles.danger,      
      },      
      size: {        
        sm: styles.sm,        
        md: styles.md,        
        lg: styles.lg,      
      },    
    },    
    compoundVariants: [      
      {        
        variant: "solid",        
        type: "danger",        
        className: styles["solid-danger"],      
      },      
      {        
        variant: "solid",        
        type: "success",        
        className: styles["solid-success"],      
      },      
      {        
        variant: "outline",        
        type: "primary",        
        className: styles["outline-primary"],      
      },      
      {        
        variant: "outline",        
        type: "danger",
        className: styles["outline-danger"],
      },      
      {        
        variant: "outline",        
        type: "success",        
        className: styles["outline-success"],      
      },      
      {        
        variant: "text",        
        type: "primary",        
        className: styles["text-primary"],      
      },      
      {        
        variant: "text",
        type: "danger",        
        className: styles["text-danger"],      
      },      
      {        
        variant: "text",
        type: "success", 
        className: styles["text-success"],  
      },    
    ],  
    defaultVariants: {    
      variant: "solid",    
      size: "md",   
    },  
  });

  return  <button className={btnStyles({ variant, type, size })}>{children}</button>  
}

export default Button;

在上面的代码片段中,我们调用了 cva 函数,传递了两个参数,并将其返回值存储在 buttonStyles 变量中,然后调用该变量以返回适当的类。让我们分解传递给函数的每个参数:

第一个参数是 CSS 类,在每次渲染 Button 组件时都会应用。这可以是一个字符串或一个类名数组。

第二个参数是一个包含三个属性的对象:variants、compoundVariants 和 defaultVariant。

  • variants 键映射到一个包含各种 props 作为键的对象。每个 prop 进一步定义其可能的值和相应的 CSS 类,当 prop 匹配这些值之一时应该应用这些类。
  • compoundVariants 属性是一个对象数组,每个对象定义了一组有效的 prop 值和相应的 CSS 类,当 prop 值匹配 compoundVariants 数组中的任何定义组合时应用这些类。
  • defaultVariant 属性包含默认 CSS 类的值,当 Button 组件渲染时,如果缺少 prop 值或没有传递 props,则应用这些类。

保存文件后,我们会得到同样的按钮:

undefined(https

://res.cloudinary.com/dz209s6jk/image/upload/v1705575169/Admin/jwpu2fp5t1vxdejyonv8.jpg)

cva 库的优点

  • 代码清晰度:显式设置类可以增强代码清晰度,使您的应用程序更易于理解和调试。
  • 更大的控制力:通过显式定义每个 prop 值和组合的类,此方法为您提供了更大的组件外观控制权。
  • 消除意外的副作用:cva 库的语法通过确保类根据组件的 prop 值设置,消除了意外的副作用。这防止了像使用 clsx 库时应用未定义类的问题。

cva 库的缺点

  • 学习曲线:需要时间学习如何有效使用 cva 库,但学习曲线并不陡峭。
  • 冗长的样板代码:此方法需要对复合变体进行显式定义,从而增加了入门所需的代码量。
  • 额外依赖:cva 是项目的一个额外依赖项,需要考虑这一点。

结论

高效管理条件样式类的应用对于构建可扩展和可维护的 React 组件非常重要。在本文中,我们探讨了在 React 应用程序中管理条件样式类应用的三种有效方法。希望通过列出的优缺点能帮助您决定下一个项目的合适方法。

选择合适的方法取决于项目的规模、复杂性和个人偏好。手动方法由于其简单性和没有学习曲线,并且不会为项目增加额外的依赖,是小型个人项目的良好选择。如果额外的依赖项和学习库所需的时间不是问题,那么 clsx 是更好的选择,因为它提供了更易于理解的语法,使调试您的应用程序比手动方法更容易。class-variance-authority 方法是一个更好的替代方案,如果您需要一种确定的方式来了解在项目中任何给定的 prop 组合将渲染什么类型的元素。

此外,使用 CSS 模块、像 Material UI (MUI) 这样的样式组件库或像 Tailwind CSS 这样的 CSS 框架来为组件设置样式,可以提高 React 项目的整体可维护性,因为这些样式选项保持样式的隔离,帮助防止样式冲突。

最终,选择哪种方法应该与项目的具体需求和您的开发偏好一致。

本文翻译自 Frontend Mentor: How to efficiently manage CSS classes in React,旨在帮助读者了解如何在 React 应用中高效地管理条件样式类的应用。欢迎大家提出宝贵意见和建议,以便进一步完善和改进。

本文系外文翻译,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文系外文翻译前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前提条件
  • 项目设置
  • 方法一:手动方法
    • 手动方法的优点
      • 手动方法的缺点
      • 方法二:使用 clsx 库
        • 使用 clsx 库方法的优点
          • 使用 clsx 库方法的缺点
          • 方法三:使用 class-variance-authority 库
            • cva 库的优点
              • cva 库的缺点
              • 结论
              相关产品与服务
              云开发 CLI 工具
              云开发 CLI 工具(Cloudbase CLI Devtools,CCLID)是云开发官方指定的 CLI 工具,可以帮助开发者快速构建 Serverless 应用。CLI 工具提供能力包括文件储存的管理、云函数的部署、模板项目的创建、HTTP Service、静态网站托管等,您可以专注于编码,无需在平台中切换各类配置。
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档