在使用 React 构建应用程序时,我们通常希望组件能够根据用户交互动态改变其外观。通过使用条件样式类(conditional CSS classes),可以轻松实现这些变化,这些类根据特定条件进行应用或移除。
在 React 中,这些类通常根据组件的 prop 值或状态进行应用。三元运算符经常用于管理这些类的应用。下面的代码片段展示了这种常见方法的示例:
import styles from "./button.module.css"
function Button({variant}){
return (
<button className={`${styles.base} ${variant ? styles[variant] : ""}`}>
{children}
</button>
)
}
export default Button;
这种方法对于构建小型组件是典型的。然而,随着组件变得更加灵活,并且引入更多的 props 来处理这种灵活性,跟踪可用的 CSS 类及其应用条件变得困难。按钮组件可能最终会变成这样:
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 应用程序中管理条件样式类的高效技术。
为了充分利用本文内容,您需要:
我们将构建一个按钮组件,具有以下 props:
要跟随本文一起操作,您需要创建一个新的 React 应用程序。您可以在终端中执行以下命令来完成此操作:
npm create vite@latest <project_name> -- --template react
项目创建完成后,切换到您的项目目录,并执行以下命令以安装项目所需的依赖项:
npm install
安装必要的依赖项后,让我们对新的 React 应用程序进行一些更改。首先,删除 App.css 文件。我们不需要它,因为我们将使用 CSS 模块来为按钮组件设置样式。
接下来,在 src 目录内创建一个新的 components 目录。然后,在 components 目录中创建两个新文件:Button.jsx 和 button.module.css。
将以下 CSS 样式复制并粘贴到 button.module.css 文件中:
/* 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 样式:
* {
padding: 0;
margin: 0;
box-sizing: border-box;
}
body {
min-height: 100vh;
display: grid;
place-items: center;
}
接下来,将以下代码复制并粘贴到 Button.jsx 文件中:
import styles from "./button.module.css"
function Button(){
return <button className={styles.base}>Button</button>
}
export default Button;
接下来,用以下内容替换 App.jsx 文件中的内容:
import Button from "./components/Button";
function App() {
return <Button />;
}
export default App;
最后,保存所有更改并执行以下命令以启动开发服务器:
npm run dev
您应该会在屏幕上看到如下按钮:
项目设置完成后,让我们来看一下在 React 中高效管理条件样式类应用的不同方法。
手动方法涉及创建一个 CSS 类数组,然后使用 Array.join() 方法将这些类连接成一个字符串,该字符串将应用于组件。将此方法实现到我们的按钮组件中:
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() 方法:
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>;
}
保存更改后,我们得到这样的按钮:
当我们在浏览器的开发者工具中检查该元素时:
这些类被逗号分隔,并作为单个类而不是单独的类应用于按钮上。使用 join() 方法时,我们可以传递一个分隔符作为参数,在这种情况下,当我们调用 join() 方法时,使用空格作为分隔符。
当我们还原更改并保存文件后,在浏览器中我们会得到一个漂亮的按钮:
undefined(
https://res.cloudinary.com/dz209s6jk/image/upload/v1705575169/Admin/jwpu2fp5t1vxdejyonv8.jpg)
clsx 是一个轻量级的实用库,用于管理 CSS 类的应用。它是一个简单的函数,接受对象、数组或字符串作为参数,并根据提供的条件返回有效类的字符串插值。
在终端中执行以下命令以安装 clsx 库:
npm install clsx
安装 clsx 库后,让我们重构我们的 Button 组件:
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 值时才会应用这些类。
保存文件后,您将得到同样漂亮的按钮:
这种方法可以进一步优化,在应用相应的 CSS 类之前检查 prop 是否具有有效值,而不是在 prop 的值为 true 时应用与任何 prop 相关联的 CSS 类。这有助于避免由于向组件的任何 prop 传递无效值而导致应用未定义类的情况。要优化此方法,我们可以按如下方式进行:
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 类。
class-variance-authority(cva)是另一个用于管理组件中 CSS 类条件应用的实用库。cva 和 clsx 之间的关键区别在于,需要在 cva 中显式指定在渲染组件时根据不同 props 值的存在和组合应用于组件的样式。
在终端中执行以下命令以安装 cva 库:
npm install class-variance-authority
重构 Button 组件后,我们现在有:
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)
高效管理条件样式类的应用对于构建可扩展和可维护的 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 删除。