在 React 中,可以使用多种方式编写 CSS 样式,没有一个标准的方式。本文旨在为大家梳理一个整体的CSS编写思路,介绍了五种React中编写CSS的方式,希望读完之后能对React中编写CSS有一个整体的认识。
组件化开发模式下的CSS
组件化开发已经成为前端领域默认的一种开发方式,然而CSS在起初的设计中并没有考虑到现代前端组件化的需求。每个页面有自己的全局样式表,样式规则可以应用于整个页面。这种方式在复杂的应用程序中,随着组件的引入和复用,全局样式表可能导致以下问题:
- 样式冲突: 当多个组件都依赖于全局样式表时,可能会发生样式冲突,其中一个组件的样式规则意外地影响了其他组件。
- 可维护性: 维护大型全局样式表变得更加困难,因为开发人员必须谨慎选择类名以避免冲突,并跟踪哪些样式适用于哪些组件。
- 复杂性: 随着应用程序的增长,全局样式表可能变得庞大而复杂,难以理解和修改。
- 复用困难: 无法轻松地将样式与组件一起打包,以便在不同项目中共享或复用。
因此需要新的CSS解决方案来支持组件化开发模式,因为传统的全局 CSS 适用性原则与组件化开发不兼容。并且需要满足以下最基本的特性:
- 局部 CSS : 组件应该具备自包含性,这意味着组件的样式应该只影响组件自身,而不会影响到其他组件或全局样式。这可以通过使用 CSS 模块、styled-components 或其他 CSS-in-JS 解决方案来实现。这些工具将样式限定在组件范围内,防止全局样式污染。
- 动态 CSS : 在组件化开发中,通常需要根据组件的状态或属性动态地修改样式。这可以通过条件渲染类名、样式变量、模板字符串等方式来实现。React 的条件渲染和状态管理机制非常适合处理这些情况。
- 支持所有 CSS 特性: CSS 在组件化开发中依然具有全面的表现力。你可以使用 CSS 来控制布局、颜色、动画、响应式设计等方面的样式。确保你所选择的 CSS 解决方案能够支持所有这些特性。
- 样式复用: 组件化开发中,通常需要在不同组件之间共享样式。这可以通过创建共享的样式模块或定义可复用的样式组件来实现。一些 CSS-in-JS 库提供了复用样式的机制,例如创建主题、样式混合等。
React下的CSS编写方式
方式一:内联样式的CSS
在React中,可以使用内联样式(Inline Styles)来将CSS样式直接应用于组件的元素。内联样式是将样式以JavaScript对象 的形式 传递给React组件元素的style属性的一种方式。
需要注意的是,内联样式中的样式属性名称采用驼峰命名法,与常规CSS属性名称略有不同。例如,background-color 在内联样式中写作 backgroundColor。
以下是使用内联样式编写CSS的基本方法:
javascript
import React from 'react';
class MyComponent extends React.Component {
render() {
// 创建一个样式对象
const myStyle = {
color: 'blue',
backgroundColor: 'lightgray',
padding: '10px',
};
return (
<div>
{/* 将样式对象应用于组件元素 */}
<h1 style={myStyle}>Hello, React!</h1>
</div>
);
}
}
export default MyComponent;
内联样式的主要优点是它允许动态生成样式,可以根据组件的状态和属性来设置样式属性
javascript
import React from 'react';
class MyComponent extends React.Component {
render() {
// 根据条件动态生成样式对象
const dynamicStyle = {
color: this.props.isError ? 'red' : 'blue',
fontWeight: this.props.isBold ? 'bold' : 'normal',
};
return (
<div>
{/* 应用动态生成的样式 */}
<p style={dynamicStyle}>Dynamic Styles</p>
</div>
);
}
}
export default MyComponent;
下面案例展示了如何使用内联样式以及如何根据组件的状态来动态改变样式属性。
fontSize 样式属性会根据 titleSize 变量的值来动态计算。
javascript
import React, { PureComponent } from "react";
export class App extends PureComponent {
constructor() {
super();
this.state = {
titleSize: 30,
};
}
addTitleSize() {
const titleSize = this.state.titleSize + 3;
this.setState({ titleSize });
}
render() {
const { titleSize } = this.state;
return (
<div>
{/* 内联样式 */}
<h2 style={{ color: "#2e3bf7", fontSize: `${titleSize}px` }}>
我是标题
</h2>
<p style={{ color: "skyblue", fontSize: "20px" }}>我是内容</p>
<button onClick={(e) => this.addTitleSize()}>增加标题</button>
</div>
);
}
}
export default App;
方式二:普通CSS编写
在React中,可以使用传统的CSS文件来为组件编写样式,适用于不需要特别高级的样式解决方案的项目。
下面是在React中使用传统CSS文件的一般步骤:
1.创建一个CSS文件:首先,创建一个以.css为扩展名的CSS文件。这个文件可以位于你的项目的任何位置,但通常会放在组件所在的同一目录中或专门的样式目录中。
css
/* styles.css */
.my-component {
background-color: lightblue;
padding: 10px;
}
2.在组件中引入CSS文件:在你的React组件中引入上述CSS文件,你可以使用相对路径或绝对路径来引入。
javascript
import React from "react";
import "./styles.css"; // 引入样式文件
function MyComponent() {
return <div className="my-component">Hello, React!</div>;
}
export default MyComponent;
3.应用样式类名:在组件的JSX中使用className属性,将样式类名应用到需要样式的元素上。
javascript
function MyComponent() {
return <div className="my-component">Hello, React!</div>;
}
这种方式在应用规模变大时,可能会遇到样式冲突和维护困难的问题。
方式三:CSS Modules
React 中使用模块化 CSS(也叫 CSS Modules)进行开发是一种很好的实践,可以避免全局 CSS 样式冲突问题,并将样式与组件关联起来。
1.安装依赖
确保项目中已经安装了支持 CSS Modules 的 CSS 预处理器,比如 css-loader 和 style-loader(通常是 Webpack 的一部分)
2.创建CSS模块文件
在项目中创建一个 CSS 文件,命名规则通常是 [文件名].module.css。这个命名约定告诉 Webpack 使用 CSS Modules 处理这个文件。例如,可以创建一个 App.module.css 文件:
css
/* App.module.css */
.title {
color: red;
}
.content {
font-size: 16px;
}
3. 在 React 组件中使用 CSS Modules
在React 组件中,可以使用 import 导入 CSS 模块,然后将模块中的类名用作 JSX 元素的 className 属性。这样,只有当前组件范围内的样式类会应用到元素上。例如:
javascript
import React, { PureComponent } from "react";
import appStyle from "./App.module.css";
class App extends PureComponent {
render() {
return (
<div>
<h2 className={appStyle.title}>我是标题 (标题样式是红色)</h2>
<p className={appStyle.content}>我是内容</p>
</div>
);
}
}
export default App;
使用模块化 CSS 的好处在于可以在组件级别管理样式,不必担心全局样式冲突。这种方式使得代码更加模块化和可维护,并且有助于提高团队协作效率。
但是模块化CSS并非十全十美,它也有自己的一些弊端:
- 不方便动态修改某些样式: 模块化 CSS 的一个特点是类名通常在编译时固定,很难在运行时动态修改某些样式。如果需要根据用户输入或应用状态更改样式,就需要使用内联样式或其他方法,这可能会变得复杂。
- 不能使用连接符: 在传统的全局 CSS 中,可以使用连接符(如 "-" 或 "_")来编写类名,以便更好地描述样式的含义。在模块化 CSS 中,类名通常采用驼峰式或其他格式,这可能使类名看起来不够直观。
方式四:Less编写
在 React 中使用 Less 编写 CSS 允许以一种更具可维护性和可复用性的方式管理样式。Less 是一种CSS预处理器,它引入了许多功能,如变量、嵌套、混合和函数,以帮助简化和组织CSS代码。
当在 Create React App(CRA)项目中使用 Less 时,可以按照以下步骤进行配置:
1.安装 Less 相关依赖
安装Create React App 的配置扩展工具
bash
npm install @craco/craco
2.创建 craco.config.js 文件
在项目的根目录下,创建一个 craco.config.js 文件。然后,在 craco.config.js 文件中配置 Less 支持,示例如下:
使用 craco-less 插件配置 Less 支持,并可以在 lessOptions 下配置 Less 的选项。在这里,我们还可以修改 Ant Design 的主题色,使其适应项目的需求。
java
const CracoLessPlugin = require("craco-less");
module.exports = {
plugins: [
{
plugin: CracoLessPlugin,
options: {
lessLoaderOptions: {
lessOptions: {
/* 在此处配置 Less 的选项 */
modifyVars: { "@primary-color": "#1890ff" }, // 修改 Ant Design 的主题色
javascriptEnabled: true, // 允许在 Less 中使用 JavaScript 表达式
},
},
},
},
],
};
3.编写Less文件
创建 .less 文件,编写您的 Less 样式。例如:
less
/* App.less */
@primaryColor: pink;
.section {
border: 1px solid @primaryColor;
.title {
font-size: 30px;
color: @primaryColor;
}
.content {
font-size: 18px;
color: @primaryColor;
}
}
4.在组件中引入 Less 文件
在您的 React 组件中,使用 import 语句引入 Less 文件,就像引入普通 CSS 文件一样。然后,您可以在组件中使用 Less 定义的样式类。例如:
javascript
import React, { PureComponent } from "react";
import "./App.less";
export class App extends PureComponent {
render() {
return (
<div>
<div className="section">
<h2 className="title">我是标题</h2>
<p className="content">我是内容</p>
</div>
</div>
);
}
}
export default App;
方式五: css-in-js
什么是css-in-js
首先需要明确,css-in-js是一种开发模式,将样式css也写入到JavaScript中的方式 。在React中,使用CSS-in-JS库编写CSS可以将组件的样式和组件本身紧密集成在一起,提供了更高的可维护性和可重用性。
使用CSS-in-JS库编写CSS可以提供更好的封装性,避免全局作用域的样式冲突,使组件更加独立和可重用。不过,选择合适的CSS-in-JS库并熟悉其语法和特性是很重要的,以确保在项目中有效地使用它们。下文将以Styled-components为例展开说明。
styled-components简介
Styled-components 是一个用于编写 CSS-in-JS 的库,允许在 JavaScript 或 TypeScript 中编写样式,以一种组件化的方式,将样式与组件紧密耦合在一起。
这个库的核心思想是,将组件与其样式定义放在同一个文件中,使得组件的样式变得可维护、可复用,并且避免了全局 CSS 样式的命名冲突问题。
styled-components 的主要特性在于:
- 组件样式化:styled-components 允许将样式与组件相关联。可以使用它来创建一个样式化的 React 组件,这个组件的样式定义就在组件自身的代码中,而不是放在一个独立的 CSS 文件中。
- 动态样式:你可以根据组件的 props 动态设置样式。这意味着你可以根据组件的状态或属性值来改变样式,而不必编写大量的条件 CSS 类名。
- 模板字符串样式:Styled-components 使用了模板字符串来定义样式。这使得在样式中嵌入 JavaScript 表达式非常方便。你可以使用 JavaScript 变量、函数等来生成样式。
标签模版字符串
标签模板字符串(Tagged Template Strings)是一种 JavaScript 语言特性,它允许自定义字符串模板的处理方式。
在 React 和 Styled-components 中,标签模板字符串通常用于处理样式,允许在 JavaScript 中嵌入 CSS 样式,并在组件中使用这些样式。
需要注意,styled.xxx
最终会返回一个组件,可以在模版字符串里编写css样式,但是正常没有高亮提示,因此需要安装对应插件
javascript
import styled from "styled-components";
const Button = styled.button`
background-color: ${props => (props.primary ? "blue" : "white")};
color: ${props => (props.primary ? "white" : "blue")};
`;
function App() {
return (
<div>
<Button primary>Primary Button</Button>
<Button>Secondary Button</Button>
</div>
);
}
styled-components基本使用步骤
1. 安装 Styled-components:
csharp
npm install styled-components
# 或
yarn add styled-components
2.导入 Styled-components:
在组件文件中,导入 styled 对象和必要的样式变量:
javascript
import styled from "styled-components";
3. 创建一个样式化组件:
使用 styled 对象创建一个样式化组件。使用 ES6 模板字符串语法来定义样式:
css
const Button = styled.button`
background-color: #007bff;
color: #fff;
padding: 10px 20px;
border: none;
border-radius: 4px;
`;
4. 使用样式化组件:
可以像使用普通的 React 组件一样使用这个样式化组件。例如,可以在 JSX 中渲染它:Styled-components 会自动将你的样式应用到这个按钮。
css
<Button>Click me</Button>
5.设置动态样式
.attrs() 方法:这个方法用于添加属性到组件。它接受一个函数作为参数,这个函数会在组件渲染时被调用。这个函数的目的是为组件提供属性,
下方案例中为 tColor 属性提供了默认值,如果 props 中没有 color 属性,它将默认为 "blue" 。
模板字符串中的 ${}
语法:允许在 CSS 样式中插入 JavaScript 表达式。例如,在下方案例中${props => props.size}px 会根据组件的 size 属性来动态设置 .title 的字体大小。
middleSize 和 primaryColor 变量:这些变量被用在模板字符串中,这是 styled-components 的一大特点,它允许你将 JavaScript 变量引入 CSS 样式中。
css
import { styled } from "styled-components";
import { middleSize, primaryColor } from "./style/variables"
export const SectionWrapper = styled.div.attrs(props => {
// 注意写法
return {
tColor: props.color || "blue"
}
})`
color: #1ebf97;
border: 1px solid black;
.title {
font-size: ${props => props.size}px;
color: ${props => props.tColor};
}
/* 可以引入js文件中的变量 */
.content {
font-size: ${middleSize};
color: ${primaryColor};
}
`;
可以在jsx中使用该样式组件
xml
<SectionWrapper size={size} color={color}>
<h2 className="title">我是标题</h2>
<p className="content">我是内容</p>
</SectionWrapper>
styled-components共享主题数据
styled-components 允许在整个应用程序中共享主题数据,。主题是一个包含样式属性的 JavaScript 对象,开发人员可以在该对象内自定义颜色、字体、间距等样式属性。通过使用主题,可以轻松在应用程序中维护一致的外观。
1.创建主题对象
创建一个包含应用程序中所有共享主题数据的主题对象。这个对象通常包括颜色、字体、间距等与样式相关的信息,以确保在样式组件中可以正确使用主题数据。
css
const theme = {
colors: {
primary: 'blue',
secondary: 'green',
},
fonts: {
body: 'Arial, sans-serif',
heading: 'Helvetica, sans-serif',
},
};
2.创建 ThemeProvider
使用 styled-components 库中的 ThemeProvider 组件来将主题数据提供给整个应用程序。这通常在应用程序的最高级别组件处完成。例如:
javascript
root.render(
<React.StrictMode>
{/* 设置主题来共享数据 */}
<ThemeProvider theme={theme}>
<App />
</ThemeProvider>
</React.StrictMode>
);
3.在样式组件中使用主题数据
在项目开发的样式组件中,可以通过 props 访问主题数据。首先,确保你的组件引入了 styled-components 并使用 props.theme 来访问主题数据。props.theme 中会包含主题数据,可以使用它来设置按钮的背景颜色、文本颜色和字体。
例如: ${props => props.theme.colors.primary}
ini
import styled from 'styled-components';
const Button = styled.button`
background-color: ${props => props.theme.colors.primary};
color: white;
font-family: ${props => props.theme.fonts.body};
`;
4.使用主题数据
使用样式组件,并确保 ThemeProvider 包装了这些组件。在组件中将能够访问主题数据并应用它们。例如:
ini
export const HomeWrapper = styled.div`
.title {
/* 使用styled-components中的ThemeProvider提供的数据 */
color: ${props => props.theme.homeColor};
font-size: ${props => props.theme.homeSize};
}
`
补充:React添加class的方式
1.通过模版字符串
可以使用 JavaScript 模板字符串来动态生成类名,并将其分配给 className 属性。这在需要根据条件添加类名时非常有用。
scala
import React, { PureComponent } from "react";
import classNames from "classnames";
export class App extends PureComponent {
constructor() {
super();
this.state = {
isB: true,
isC: false,
};
}
render() {
// isB isC 代表是否添加b类以及c类
const { isB, isC } = this.state;
return (
<div>
{/* 方式一:通过模版字符串 */}
<h2 className={`classA ${isB ? "classB" : ""} ${isC ? "classC" : ""}`}>
方式一:通过模版字符串添加class
</h2>
</div>
);
}
}
export default App;
2.通过生成数组来添加class
通过创建一个类名数组 classList,然后根据条件将类名添加到数组中,最后使用 join 方法将数组转换为字符串,然后将其分配给 className 属性。
ini
const classList = ["classA"];
if (isB) classList.push("classB");
if (isC) classList.push("classC");
const classname = classList.join(" ");
<h2 className={classname}>方式二:通过数组添加 class</h2>
3.通过"classnames"库添加class
3.1 classnames库用法
使用 classnames 函数来动态生成类名。这个函数接受多个参数,可以是字符串或对象,用于根据条件生成类名。
字符串参数:可以传递一个或多个字符串作为参数,它们将被简单地连接在一起。
ini
const className = classNames('button', 'primary', 'large');
// className = 'button primary large'
对象参数:可以传递一个对象,其中对象的属性名表示类名,属性值表示是否应包括该类名。
ini
const isActive = true;
const isDisabled = false;
const className = classNames({
button: true,
primary: isActive,
disabled: isDisabled,
});
// className = 'button primary'
3.2案例
下列案例使用了第三方库 classnames,它是一个用于处理类名的工具库。可以传递一个对象,根据条件来确定是否添加类名。它提供了一种更方便和可读性更高的方式来处理动态类名。
classnames 会根据 isB 和 isC 的值自动添加或移除相应的类名。这种方式通常是最推荐的,特别是在需要处理多个类名和多个条件的情况下,它可以提高代码的可维护性。
javascript
import classNames from "classnames";
<h2 className={classNames("classA", { classB: isB, classC: isC })}>
方式三:通过 classnames 库
</h2>