React学习教程,从入门到精通, React 样式语法知识点与案例详解(13)
React样式语法知识点与案例详解 本文全面介绍React中的样式方法,包含8种主要技术: 行内样式 - 使用JS对象和驼峰命名 CSS样式表 - 传统方式引入 CSS Modules - 局部作用域CSS CSS-in-JS - 如Styled-components Sass/SCSS - 预处理器功能 Tailwind CSS - 实用类框架 条件样式 - 动态应用样式 动态样式 。
·
React 样式语法知识点与案例详解
作为React初学者,掌握样式语法是构建美观UI的关键。本文将详细介绍React中所有主要的样式方法,并提供详细注释的案例代码。
一、React样式语法知识点总览
1. 行内样式 (Inline Styles)
- 使用
style
属性,值为JavaScript对象 - 属性名采用驼峰命名法
- 值为字符串或数字
2. CSS样式表 (CSS Stylesheets)
- 传统的CSS文件引入方式
- 使用
className
而非class
3. CSS Modules
- 局部作用域的CSS
- 避免样式冲突
- 文件名通常为
[name].module.css
4. CSS-in-JS库 (Styled-components)
- 在JavaScript中编写CSS
- 动态样式支持良好
- 组件即样式
5. 使用Sass/SCSS预处理器
- 支持变量、嵌套、混合等功能
- 文件扩展名为
.scss
或.sass
6. 使用Tailwind CSS
- 实用优先的CSS框架
- 直接在className中使用实用类
7. 条件样式
- 根据状态或props动态应用样式
- 使用三元运算符或逻辑运算符
8. 动态样式
- 根据数据或用户交互改变样式
- 结合状态管理使用
二、详细案例代码
案例1:行内样式 (Inline Styles)
import React, { useState } from 'react';
function InlineStyleExample() {
// 定义样式对象 - 注意驼峰命名法
const containerStyle = {
padding: '20px',
backgroundColor: '#f0f0f0',
borderRadius: '8px',
margin: '10px 0',
fontFamily: 'Arial, sans-serif'
};
const buttonStyle = {
backgroundColor: '#007bff',
color: 'white',
border: 'none',
padding: '10px 20px',
borderRadius: '4px',
cursor: 'pointer',
fontSize: '16px',
marginRight: '10px'
};
const hoverButtonStyle = {
...buttonStyle, // 使用展开运算符继承基础样式
backgroundColor: '#0056b3', // 覆盖背景色
transform: 'scale(1.05)', // 添加缩放效果
transition: 'all 0.3s ease' // 添加过渡效果
};
// 状态管理用于动态样式
const [isHovered, setIsHovered] = useState(false);
const [fontSize, setFontSize] = useState(16);
return (
<div style={containerStyle}>
<h2 style={{
color: '#333',
marginBottom: '20px',
textAlign: 'center'
}}>
行内样式示例
</h2>
{/* 基础按钮样式 */}
<button
style={buttonStyle}
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
>
基础按钮
</button>
{/* 动态按钮样式 - 根据hover状态改变 */}
<button
style={isHovered ? hoverButtonStyle : buttonStyle}
>
{isHovered ? '鼠标悬停中' : '悬停我'}
</button>
{/* 根据状态动态改变字体大小 */}
<div style={{ marginTop: '20px' }}>
<p style={{
fontSize: `${fontSize}px`,
transition: 'font-size 0.3s ease'
}}>
当前字体大小: {fontSize}px
</p>
<button
style={buttonStyle}
onClick={() => setFontSize(prev => Math.min(prev + 2, 32))}
>
增大字体
</button>
<button
style={buttonStyle}
onClick={() => setFontSize(prev => Math.max(prev - 2, 12))}
>
减小字体
</button>
</div>
{/* 复杂的动态样式示例 */}
<div style={{
marginTop: '30px',
padding: '15px',
backgroundColor: isHovered ? '#e9ecef' : '#ffffff',
border: `2px solid ${isHovered ? '#007bff' : '#dee2e6'}`,
borderRadius: '8px',
transition: 'all 0.3s ease',
cursor: 'pointer'
}}
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
>
<p>这个盒子的样式会根据鼠标悬停状态动态改变</p>
<p style={{ fontStyle: 'italic', color: '#6c757d' }}>
{isHovered ? '鼠标正在悬停' : '请将鼠标悬停在此处'}
</p>
</div>
</div>
);
}
export default InlineStyleExample;
案例2:CSS样式表 (CSS Stylesheets)
首先创建 StylesheetExample.css
文件:
/* StylesheetExample.css */
.card-container {
padding: 20px;
background-color: #f8f9fa;
border-radius: 8px;
margin: 20px 0;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
font-family: 'Arial', sans-serif;
}
.card-title {
color: #343a40;
font-size: 24px;
margin-bottom: 15px;
text-align: center;
border-bottom: 2px solid #007bff;
padding-bottom: 10px;
}
.card-content {
background-color: white;
padding: 15px;
border-radius: 4px;
margin: 15px 0;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}
.button-group {
display: flex;
justify-content: center;
gap: 10px;
margin-top: 20px;
}
.btn {
padding: 10px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
transition: all 0.3s ease;
}
.btn-primary {
background-color: #007bff;
color: white;
}
.btn-primary:hover {
background-color: #0056b3;
transform: translateY(-2px);
}
.btn-secondary {
background-color: #6c757d;
color: white;
}
.btn-secondary:hover {
background-color: #545b62;
transform: translateY(-2px);
}
.alert-box {
padding: 15px;
margin: 15px 0;
border-radius: 4px;
font-weight: bold;
}
.alert-success {
background-color: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.alert-danger {
background-color: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
/* 响应式设计 */
@media (max-width: 768px) {
.card-container {
padding: 15px;
margin: 10px 0;
}
.button-group {
flex-direction: column;
align-items: center;
}
.btn {
width: 100%;
max-width: 200px;
}
}
然后创建 React 组件:
import React, { useState } from 'react';
import './StylesheetExample.css'; // 导入CSS文件
function StylesheetExample() {
const [message, setMessage] = useState('');
const [messageType, setMessageType] = useState('');
const [count, setCount] = useState(0);
const handlePrimaryClick = () => {
setCount(count + 1);
setMessage(`点击了 ${count + 1} 次主按钮`);
setMessageType('success');
};
const handleSecondaryClick = () => {
setCount(count - 1);
setMessage(`点击了 ${count - 1} 次次按钮`);
setMessageType('danger');
};
const resetCount = () => {
setCount(0);
setMessage('计数器已重置');
setMessageType('success');
};
return (
<div className="card-container">
<h2 className="card-title">CSS样式表示例</h2>
<div className="card-content">
<p>当前计数: <strong>{count}</strong></p>
{/* 条件渲染alert消息 */}
{message && (
<div className={`alert-box alert-${messageType}`}>
{message}
</div>
)}
<div className="button-group">
<button className="btn btn-primary" onClick={handlePrimaryClick}>
主按钮 (+1)
</button>
<button className="btn btn-secondary" onClick={handleSecondaryClick}>
次按钮 (-1)
</button>
<button className="btn btn-secondary" onClick={resetCount}>
重置
</button>
</div>
</div>
{/* 条件样式示例 */}
<div className="card-content">
<h3>条件样式示例</h3>
<p>根据计数值改变样式:</p>
<div
className="alert-box"
style={{
backgroundColor: count > 0 ? '#d4edda' : count < 0 ? '#f8d7da' : '#fff3cd',
color: count > 0 ? '#155724' : count < 0 ? '#721c24' : '#856404',
border: count > 0 ? '1px solid #c3e6cb' : count < 0 ? '1px solid #f5c6cb' : '1px solid #ffeaa7'
}}
>
{count > 0 ? '正数状态' : count < 0 ? '负数状态' : '零状态'}
</div>
</div>
</div>
);
}
export default StylesheetExample;
案例3:CSS Modules
首先创建 CSSModulesExample.module.css
文件:
/* CSSModulesExample.module.css */
.container {
padding: 20px;
background-color: #e9ecef;
border-radius: 8px;
margin: 20px 0;
font-family: 'Arial', sans-serif;
}
.title {
color: #28a745;
font-size: 24px;
margin-bottom: 15px;
text-align: center;
border-bottom: 2px solid #28a745;
padding-bottom: 10px;
}
.contentBox {
background-color: white;
padding: 20px;
border-radius: 6px;
margin: 15px 0;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.buttonContainer {
display: flex;
justify-content: space-around;
flex-wrap: wrap;
gap: 10px;
margin-top: 20px;
}
.primaryBtn {
padding: 12px 24px;
background-color: #28a745;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
transition: all 0.3s ease;
}
.primaryBtn:hover {
background-color: #218838;
transform: scale(1.05);
}
.dangerBtn {
padding: 12px 24px;
background-color: #dc3545;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
transition: all 0.3s ease;
}
.dangerBtn:hover {
background-color: #c82333;
transform: scale(1.05);
}
.infoBox {
padding: 15px;
margin: 15px 0;
border-radius: 4px;
font-weight: 500;
}
.info {
composes: infoBox;
background-color: #cce5ff;
color: #004085;
border: 1px solid #b8daff;
}
.warning {
composes: infoBox;
background-color: #fff3cd;
color: #856404;
border: 1px solid #ffeaa7;
}
.success {
composes: infoBox;
background-color: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
/* 动画效果 */
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
.animatedBox {
animation: fadeIn 0.5s ease-out;
}
/* 响应式设计 */
@media (max-width: 768px) {
.container {
padding: 15px;
margin: 10px 0;
}
.buttonContainer {
flex-direction: column;
align-items: center;
}
.primaryBtn, .dangerBtn {
width: 100%;
max-width: 250px;
}
}
然后创建 React 组件:
import React, { useState, useEffect } from 'react';
import styles from './CSSModulesExample.module.css'; // 导入CSS Modules
function CSSModulesExample() {
const [count, setCount] = useState(0);
const [showMessage, setShowMessage] = useState(false);
const [messageType, setMessageType] = useState('info');
const [isVisible, setIsVisible] = useState(true);
// 模拟组件加载时的动画效果
useEffect(() => {
const timer = setTimeout(() => {
setShowMessage(true);
}, 500);
return () => clearTimeout(timer);
}, []);
const increment = () => {
setCount(count + 1);
setMessageType('success');
setShowMessage(true);
// 3秒后隐藏消息
setTimeout(() => setShowMessage(false), 3000);
};
const decrement = () => {
setCount(count - 1);
setMessageType('warning');
setShowMessage(true);
setTimeout(() => setShowMessage(false), 3000);
};
const reset = () => {
setCount(0);
setMessageType('info');
setShowMessage(true);
setTimeout(() => setShowMessage(false), 3000);
};
const toggleVisibility = () => {
setIsVisible(!isVisible);
};
// 根据计数值动态选择样式
const getDynamicStyle = () => {
if (count > 0) return styles.success;
if (count < 0) return styles.warning;
return styles.info;
};
return (
<div className={styles.container}>
<h2 className={styles.title}>CSS Modules 示例</h2>
{/* 可切换可见性的内容框 */}
{isVisible && (
<div className={`${styles.contentBox} ${styles.animatedBox}`}>
<h3>计数器</h3>
<p>当前值: <strong>{count}</strong></p>
{/* 条件渲染的消息框 */}
{showMessage && (
<div className={styles[messageType]}>
{messageType === 'success' && `增加了1,现在是 ${count}`}
{messageType === 'warning' && `减少了1,现在是 ${count}`}
{messageType === 'info' && `已重置为 ${count}`}
</div>
)}
<div className={styles.buttonContainer}>
<button className={styles.primaryBtn} onClick={increment}>
增加 (+1)
</button>
<button className={styles.dangerBtn} onClick={decrement}>
减少 (-1)
</button>
<button className={styles.primaryBtn} onClick={reset}>
重置
</button>
</div>
</div>
)}
{/* 动态样式示例 */}
<div className={styles.contentBox}>
<h3>动态样式示例</h3>
<div className={getDynamicStyle()}>
{count > 0 ? '正数状态 - 成功样式' :
count < 0 ? '负数状态 - 警告样式' :
'零状态 - 信息样式'}
</div>
</div>
{/* 控制组件可见性的按钮 */}
<div className={styles.buttonContainer}>
<button
className={isVisible ? styles.dangerBtn : styles.primaryBtn}
onClick={toggleVisibility}
>
{isVisible ? '隐藏内容' : '显示内容'}
</button>
</div>
</div>
);
}
export default CSSModulesExample;
案例4:Styled-components
首先安装 styled-components:
npm install styled-components
然后创建组件:
import React, { useState } from 'react';
import styled, { keyframes, css } from 'styled-components';
// 定义动画
const fadeIn = keyframes`
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
`;
// 定义基础容器样式
const Container = styled.div`
padding: 20px;
background-color: ${props => props.theme.backgroundColor || '#f8f9fa'};
border-radius: 8px;
margin: 20px 0;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
font-family: 'Arial', sans-serif;
animation: ${fadeIn} 0.6s ease-out;
`;
// 标题样式
const Title = styled.h2`
color: ${props => props.theme.titleColor || '#343a40'};
font-size: 24px;
margin-bottom: 15px;
text-align: center;
border-bottom: 2px solid ${props => props.theme.accentColor || '#007bff'};
padding-bottom: 10px;
position: relative;
&::after {
content: '';
position: absolute;
bottom: -2px;
left: 50%;
width: 60px;
height: 2px;
background-color: ${props => props.theme.accentColor || '#007bff'};
transform: translateX(-50%);
}
`;
// 内容框样式
const ContentBox = styled.div`
background-color: white;
padding: 20px;
border-radius: 6px;
margin: 15px 0;
box-shadow: 0 2px 8px rgba(0,0,0,0.05);
transition: all 0.3s ease;
&:hover {
box-shadow: 0 4px 15px rgba(0,0,0,0.1);
transform: translateY(-2px);
}
`;
// 按钮组容器
const ButtonGroup = styled.div`
display: flex;
justify-content: center;
flex-wrap: wrap;
gap: 10px;
margin-top: 20px;
`;
// 基础按钮样式
const BaseButton = styled.button`
padding: 12px 24px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
font-weight: 500;
transition: all 0.3s ease;
position: relative;
overflow: hidden;
&:before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: rgba(255,255,255,0.2);
transition: all 0.5s ease;
}
&:hover:before {
left: 100%;
}
&:hover {
transform: translateY(-2px) scale(1.03);
box-shadow: 0 4px 15px rgba(0,0,0,0.2);
}
&:active {
transform: translateY(0) scale(0.98);
}
`;
// 主按钮样式 - 扩展BaseButton
const PrimaryButton = styled(BaseButton)`
background-color: ${props => props.theme.primaryColor || '#007bff'};
color: white;
&:hover {
background-color: ${props => props.theme.primaryHoverColor || '#0056b3'};
}
`;
// 危险按钮样式 - 扩展BaseButton
const DangerButton = styled(BaseButton)`
background-color: ${props => props.theme.dangerColor || '#dc3545'};
color: white;
&:hover {
background-color: ${props => props.theme.dangerHoverColor || '#c82333'};
}
`;
// 信息框样式 - 根据类型动态变化
const InfoBox = styled.div`
padding: 15px;
margin: 15px 0;
border-radius: 4px;
font-weight: 500;
text-align: center;
animation: ${fadeIn} 0.4s ease-out;
${props => props.type === 'success' && css`
background-color: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
border-left: 5px solid #28a745;
`}
${props => props.type === 'warning' && css`
background-color: #fff3cd;
color: #856404;
border: 1px solid #ffeaa7;
border-left: 5px solid #ffc107;
`}
${props => props.type === 'danger' && css`
background-color: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
border-left: 5px solid #dc3545;
`}
${props => props.type === 'info' && css`
background-color: #cce5ff;
color: #004085;
border: 1px solid #b8daff;
border-left: 5px solid #17a2b8;
`}
`;
// 主题配置
const theme = {
backgroundColor: '#f8f9fa',
titleColor: '#2c3e50',
accentColor: '#3498db',
primaryColor: '#3498db',
primaryHoverColor: '#2980b9',
dangerColor: '#e74c3c',
dangerHoverColor: '#c0392b'
};
function StyledComponentsExample() {
const [count, setCount] = useState(0);
const [showMessage, setShowMessage] = useState(false);
const [messageType, setMessageType] = useState('info');
const [themeMode, setThemeMode] = useState('light');
// 切换主题
const toggleTheme = () => {
setThemeMode(themeMode === 'light' ? 'dark' : 'light');
};
// 自定义暗色主题
const darkTheme = {
backgroundColor: '#2c3e50',
titleColor: '#ecf0f1',
accentColor: '#3498db',
primaryColor: '#3498db',
primaryHoverColor: '#2980b9',
dangerColor: '#e74c3c',
dangerHoverColor: '#c0392b'
};
const currentTheme = themeMode === 'dark' ? darkTheme : theme;
const increment = () => {
setCount(count + 1);
setMessageType('success');
setShowMessage(true);
setTimeout(() => setShowMessage(false), 3000);
};
const decrement = () => {
setCount(count - 1);
setMessageType('warning');
setShowMessage(true);
setTimeout(() => setShowMessage(false), 3000);
};
const reset = () => {
setCount(0);
setMessageType('info');
setShowMessage(true);
setTimeout(() => setShowMessage(false), 3000);
};
return (
<Container theme={currentTheme}>
<Title theme={currentTheme}>
Styled-components 示例
</Title>
<ContentBox>
<h3 style={{ color: currentTheme.titleColor, textAlign: 'center' }}>
计数器: {count}
</h3>
{/* 条件渲染的消息框 */}
{showMessage && (
<InfoBox type={messageType}>
{messageType === 'success' && `计数器增加到 ${count}`}
{messageType === 'warning' && `计数器减少到 ${count}`}
{messageType === 'info' && `计数器重置为 ${count}`}
</InfoBox>
)}
<ButtonGroup>
<PrimaryButton onClick={increment}>
增加 (+1)
</PrimaryButton>
<DangerButton onClick={decrement}>
减少 (-1)
</DangerButton>
<PrimaryButton onClick={reset}>
重置
</PrimaryButton>
</ButtonGroup>
</ContentBox>
{/* 主题切换示例 */}
<ContentBox>
<h3 style={{ color: currentTheme.titleColor, textAlign: 'center' }}>
主题: {themeMode === 'light' ? '浅色模式' : '深色模式'}
</h3>
<ButtonGroup>
<PrimaryButton onClick={toggleTheme}>
切换到 {themeMode === 'light' ? '深色' : '浅色'} 模式
</PrimaryButton>
</ButtonGroup>
</ContentBox>
{/* 动态样式示例 */}
<ContentBox>
<h3 style={{ color: currentTheme.titleColor, textAlign: 'center' }}>
动态样式示例
</h3>
<InfoBox
type={count > 0 ? 'success' : count < 0 ? 'danger' : 'info'}
>
{count > 0 ? '正数 - 成功样式' :
count < 0 ? '负数 - 危险样式' :
'零 - 信息样式'}
</InfoBox>
</ContentBox>
</Container>
);
}
// 包装组件以应用主题
const ThemedStyledComponentsExample = () => (
<StyledComponentsExample />
);
export default ThemedStyledComponentsExample;
案例5:Sass/SCSS 集成
首先安装 node-sass 或 sass:
npm install sass
创建 SassExample.scss
文件:
// SassExample.scss
// 定义变量
$primary-color: #007bff;
$secondary-color: #6c757d;
$success-color: #28a745;
$danger-color: #dc3545;
$warning-color: #ffc107;
$info-color: #17a2b8;
$border-radius: 8px;
$box-shadow: 0 2px 10px rgba(0,0,0,0.1);
// 混合
@mixin button-style($bg-color, $hover-color) {
padding: 12px 24px;
background-color: $bg-color;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
font-weight: 500;
transition: all 0.3s ease;
&:hover {
background-color: $hover-color;
transform: translateY(-2px) scale(1.03);
box-shadow: 0 4px 15px rgba(0,0,0,0.2);
}
&:active {
transform: translateY(0) scale(0.98);
}
}
// 容器样式
.sass-container {
padding: 20px;
background-color: #f8f9fa;
border-radius: $border-radius;
margin: 20px 0;
box-shadow: $box-shadow;
font-family: 'Arial', sans-serif;
// 嵌套样式
.title {
color: #343a40;
font-size: 24px;
margin-bottom: 15px;
text-align: center;
border-bottom: 2px solid $primary-color;
padding-bottom: 10px;
position: relative;
&::after {
content: '';
position: absolute;
bottom: -2px;
left: 50%;
width: 60px;
height: 2px;
background-color: $primary-color;
transform: translateX(-50%);
}
}
}
// 内容框
.sass-content-box {
background-color: white;
padding: 20px;
border-radius: 6px;
margin: 15px 0;
box-shadow: 0 2px 8px rgba(0,0,0,0.05);
transition: all 0.3s ease;
&:hover {
box-shadow: 0 4px 15px rgba(0,0,0,0.1);
transform: translateY(-2px);
}
// 嵌套的标题
h3 {
color: #343a40;
text-align: center;
margin-bottom: 15px;
}
}
// 按钮组
.sass-button-group {
display: flex;
justify-content: center;
flex-wrap: wrap;
gap: 10px;
margin-top: 20px;
}
// 使用混合定义按钮
.sass-primary-btn {
@include button-style($primary-color, darken($primary-color, 10%));
}
.sass-success-btn {
@include button-style($success-color, darken($success-color, 10%));
}
.sass-danger-btn {
@include button-style($danger-color, darken($danger-color, 10%));
}
.sass-warning-btn {
@include button-style($warning-color, darken($warning-color, 10%));
color: #212529; // 警告按钮文字颜色
}
// 信息框
.sass-info-box {
padding: 15px;
margin: 15px 0;
border-radius: 4px;
font-weight: 500;
text-align: center;
&.success {
background-color: lighten($success-color, 40%);
color: darken($success-color, 20%);
border: 1px solid lighten($success-color, 30%);
border-left: 5px solid $success-color;
}
&.warning {
background-color: lighten($warning-color, 40%);
color: darken($warning-color, 20%);
border: 1px solid lighten($warning-color, 30%);
border-left: 5px solid $warning-color;
}
&.danger {
background-color: lighten($danger-color, 40%);
color: darken($danger-color, 20%);
border: 1px solid lighten($danger-color, 30%);
border-left: 5px solid $danger-color;
}
&.info {
background-color: lighten($info-color, 40%);
color: darken($info-color, 20%);
border: 1px solid lighten($info-color, 30%);
border-left: 5px solid $info-color;
}
}
// 响应式设计
@media (max-width: 768px) {
.sass-container {
padding: 15px;
margin: 10px 0;
}
.sass-button-group {
flex-direction: column;
align-items: center;
}
.sass-primary-btn,
.sass-success-btn,
.sass-danger-btn,
.sass-warning-btn {
width: 100%;
max-width: 250px;
}
}
// 暗色主题
.dark-theme {
.sass-container {
background-color: #2c3e50;
.title {
color: #ecf0f1;
border-bottom-color: $info-color;
&::after {
background-color: $info-color;
}
}
}
.sass-content-box {
background-color: #34495e;
h3 {
color: #ecf0f1;
}
}
}
创建 React 组件:
import React, { useState } from 'react';
import './SassExample.scss'; // 导入SCSS文件
function SassExample() {
const [count, setCount] = useState(0);
const [showMessage, setShowMessage] = useState(false);
const [messageType, setMessageType] = useState('info');
const [theme, setTheme] = useState('light');
const increment = () => {
setCount(count + 1);
setMessageType('success');
setShowMessage(true);
setTimeout(() => setShowMessage(false), 3000);
};
const decrement = () => {
setCount(count - 1);
setMessageType('warning');
setShowMessage(true);
setTimeout(() => setShowMessage(false), 3000);
};
const reset = () => {
setCount(0);
setMessageType('info');
setShowMessage(true);
setTimeout(() => setShowMessage(false), 3000);
};
const toggleTheme = () => {
setTheme(theme === 'light' ? 'dark' : 'light');
};
return (
<div className={`sass-container ${theme === 'dark' ? 'dark-theme' : ''}`}>
<h2 className="title">Sass/SCSS 示例</h2>
<div className="sass-content-box">
<h3>计数器: {count}</h3>
{/* 条件渲染的消息框 */}
{showMessage && (
<div className={`sass-info-box ${messageType}`}>
{messageType === 'success' && `计数器增加到 ${count}`}
{messageType === 'warning' && `计数器减少到 ${count}`}
{messageType === 'info' && `计数器重置为 ${count}`}
</div>
)}
<div className="sass-button-group">
<button className="sass-primary-btn" onClick={increment}>
增加 (+1)
</button>
<button className="sass-danger-btn" onClick={decrement}>
减少 (-1)
</button>
<button className="sass-success-btn" onClick={reset}>
重置
</button>
</div>
</div>
{/* 主题切换 */}
<div className="sass-content-box">
<h3>当前主题: {theme === 'light' ? '浅色模式' : '深色模式'}</h3>
<div className="sass-button-group">
<button className="sass-warning-btn" onClick={toggleTheme}>
切换到 {theme === 'light' ? '深色' : '浅色'} 模式
</button>
</div>
</div>
{/* 动态样式示例 */}
<div className="sass-content-box">
<h3>动态样式示例</h3>
<div className={`sass-info-box ${count > 0 ? 'success' : count < 0 ? 'danger' : 'info'}`}>
{count > 0 ? '正数 - 成功样式' :
count < 0 ? '负数 - 危险样式' :
'零 - 信息样式'}
</div>
</div>
</div>
);
}
export default SassExample;
案例6:Tailwind CSS
首先安装 Tailwind CSS:
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
配置 tailwind.config.js
:
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
"./src/**/*.{js,jsx,ts,tsx}",
],
theme: {
extend: {},
},
plugins: [],
}
在 src/index.css
中添加:
@tailwind base;
@tailwind components;
@tailwind utilities;
创建组件:
import React, { useState } from 'react';
function TailwindExample() {
const [count, setCount] = useState(0);
const [showMessage, setShowMessage] = useState(false);
const [messageType, setMessageType] = useState('info');
const [darkMode, setDarkMode] = useState(false);
const increment = () => {
setCount(count + 1);
setMessageType('success');
setShowMessage(true);
setTimeout(() => setShowMessage(false), 3000);
};
const decrement = () => {
setCount(count - 1);
setMessageType('warning');
setShowMessage(true);
setTimeout(() => setShowMessage(false), 3000);
};
const reset = () => {
setCount(0);
setMessageType('info');
setShowMessage(true);
setTimeout(() => setShowMessage(false), 3000);
};
const toggleDarkMode = () => {
setDarkMode(!darkMode);
};
// 根据消息类型返回相应的Tailwind CSS类
const getMessageClasses = () => {
switch (messageType) {
case 'success':
return 'bg-green-100 border border-green-400 text-green-700';
case 'warning':
return 'bg-yellow-100 border border-yellow-400 text-yellow-700';
case 'danger':
return 'bg-red-100 border border-red-400 text-red-700';
case 'info':
return 'bg-blue-100 border border-blue-400 text-blue-700';
default:
return 'bg-gray-100 border border-gray-400 text-gray-700';
}
};
// 根据计数值返回相应的动态样式
const getDynamicClasses = () => {
if (count > 0) return 'bg-green-100 border-l-4 border-green-500 text-green-700';
if (count < 0) return 'bg-red-100 border-l-4 border-red-500 text-red-700';
return 'bg-blue-100 border-l-4 border-blue-500 text-blue-700';
};
return (
<div className={`p-6 rounded-lg shadow-lg transition-all duration-300 ${darkMode ? 'bg-gray-800 text-white' : 'bg-gray-100'}`}>
<h2 className={`text-2xl font-bold mb-4 text-center pb-2 border-b-2 ${darkMode ? 'border-blue-400 text-blue-300' : 'border-blue-500 text-gray-800'}`}>
Tailwind CSS 示例
</h2>
<div className={`p-6 rounded-lg shadow-md transition-all duration-300 hover:shadow-lg hover:-translate-y-1 ${darkMode ? 'bg-gray-700' : 'bg-white'}`}>
<h3 className={`text-xl font-semibold text-center mb-4 ${darkMode ? 'text-white' : 'text-gray-800'}`}>
计数器: <span className="font-bold">{count}</span>
</h3>
{/* 条件渲染的消息框 */}
{showMessage && (
<div className={`p-4 mb-4 rounded border-l-4 ${getMessageClasses()} animate-fade-in`}>
<p className="font-medium">
{messageType === 'success' && `计数器增加到 ${count}`}
{messageType === 'warning' && `计数器减少到 ${count}`}
{messageType === 'info' && `计数器重置为 ${count}`}
</p>
</div>
)}
<div className="flex flex-wrap justify-center gap-3 mt-6">
<button
className="px-6 py-3 bg-blue-500 hover:bg-blue-600 text-white font-medium rounded-lg shadow transition-all duration-300 transform hover:scale-105 active:scale-95 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-opacity-50"
onClick={increment}
>
增加 (+1)
</button>
<button
className="px-6 py-3 bg-red-500 hover:bg-red-600 text-white font-medium rounded-lg shadow transition-all duration-300 transform hover:scale-105 active:scale-95 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-opacity-50"
onClick={decrement}
>
减少 (-1)
</button>
<button
className="px-6 py-3 bg-green-500 hover:bg-green-600 text-white font-medium rounded-lg shadow transition-all duration-300 transform hover:scale-105 active:scale-95 focus:outline-none focus:ring-2 focus:ring-green-500 focus:ring-opacity-50"
onClick={reset}
>
重置
</button>
</div>
</div>
{/* 主题切换 */}
<div className={`mt-6 p-6 rounded-lg shadow-md transition-all duration-300 ${darkMode ? 'bg-gray-700' : 'bg-white'}`}>
<h3 className={`text-xl font-semibold text-center mb-4 ${darkMode ? 'text-white' : 'text-gray-800'}`}>
当前主题: <span className="font-bold">{darkMode ? '深色模式' : '浅色模式'}</span>
</h3>
<div className="flex justify-center">
<button
className={`px-6 py-3 font-medium rounded-lg shadow transition-all duration-300 transform hover:scale-105 active:scale-95 focus:outline-none ${
darkMode
? 'bg-yellow-500 hover:bg-yellow-600 text-white'
: 'bg-gray-800 hover:bg-gray-900 text-white'
}`}
onClick={toggleDarkMode}
>
切换到 {darkMode ? '浅色' : '深色'} 模式
</button>
</div>
</div>
{/* 动态样式示例 */}
<div className={`mt-6 p-6 rounded-lg shadow-md transition-all duration-300 ${darkMode ? 'bg-gray-700' : 'bg-white'}`}>
<h3 className={`text-xl font-semibold text-center mb-4 ${darkMode ? 'text-white' : 'text-gray-800'}`}>
动态样式示例
</h3>
<div className={`p-4 rounded border-l-4 ${getDynamicClasses()}`}>
<p className="font-medium">
{count > 0 ? '正数 - 成功样式' :
count < 0 ? '负数 - 危险样式' :
'零 - 信息样式'}
</p>
</div>
</div>
{/* 响应式设计示例 */}
<div className={`mt-6 p-6 rounded-lg shadow-md transition-all duration-300 ${darkMode ? 'bg-gray-700' : 'bg-white'}`}>
<h3 className={`text-xl font-semibold text-center mb-4 ${darkMode ? 'text-white' : 'text-gray-800'}`}>
响应式设计示例
</h3>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<div className={`p-4 rounded-lg text-center transition-all duration-300 hover:shadow-lg ${darkMode ? 'bg-gray-600 hover:bg-gray-500' : 'bg-blue-50 hover:bg-blue-100'}`}>
<h4 className="font-bold">移动端</h4>
<p className="text-sm">单列布局</p>
</div>
<div className={`p-4 rounded-lg text-center transition-all duration-300 hover:shadow-lg ${darkMode ? 'bg-gray-600 hover:bg-gray-500' : 'bg-green-50 hover:bg-green-100'}`}>
<h4 className="font-bold">平板端</h4>
<p className="text-sm">双列布局</p>
</div>
<div className={`p-4 rounded-lg text-center transition-all duration-300 hover:shadow-lg ${darkMode ? 'bg-gray-600 hover:bg-gray-500' : 'bg-purple-50 hover:bg-purple-100'}`}>
<h4 className="font-bold">桌面端</h4>
<p className="text-sm">三列布局</p>
</div>
</div>
</div>
</div>
);
}
export default TailwindExample;
案例7:综合应用 - 完整的样式实践
import React, { useState, useEffect } from 'react';
import './ComprehensiveExample.css'; // 基础CSS
import styles from './ComprehensiveExample.module.css'; // CSS Modules
// 行内样式对象
const inlineStyles = {
heroSection: {
padding: '60px 20px',
textAlign: 'center',
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
color: 'white',
borderRadius: '12px',
margin: '20px 0',
boxShadow: '0 10px 30px rgba(0,0,0,0.2)'
},
cardGrid: {
display: 'grid',
gridTemplateColumns: 'repeat(auto-fit, minmax(300px, 1fr))',
gap: '20px',
marginTop: '30px'
}
};
function ComprehensiveExample() {
const [activeTab, setActiveTab] = useState('all');
const [searchTerm, setSearchTerm] = useState('');
const [darkMode, setDarkMode] = useState(false);
const [animatedCards, setAnimatedCards] = useState([]);
// 模拟数据
const projects = [
{ id: 1, title: '电商网站', category: 'web', status: 'completed', progress: 100 },
{ id: 2, title: '移动应用', category: 'mobile', status: 'in-progress', progress: 65 },
{ id: 3, title: '数据分析平台', category: 'web', status: 'planning', progress: 20 },
{ id: 4, title: 'AI聊天机器人', category: 'ai', status: 'in-progress', progress: 80 },
{ id: 5, title: '物联网系统', category: 'iot', status: 'completed', progress: 100 },
{ id: 6, title: '区块链应用', category: 'blockchain', status: 'planning', progress: 10 }
];
// 过滤项目
const filteredProjects = projects.filter(project => {
const matchesTab = activeTab === 'all' || project.category === activeTab;
const matchesSearch = project.title.toLowerCase().includes(searchTerm.toLowerCase());
return matchesTab && matchesSearch;
});
// 切换暗色模式
const toggleDarkMode = () => {
setDarkMode(!darkMode);
};
// 动画效果
useEffect(() => {
const timer = setTimeout(() => {
setAnimatedCards(filteredProjects.map(p => p.id));
}, 300);
return () => clearTimeout(timer);
}, [filteredProjects]);
// 获取状态相关的样式类
const getStatusClass = (status) => {
switch (status) {
case 'completed': return styles.completed;
case 'in-progress': return styles.inProgress;
case 'planning': return styles.planning;
default: return '';
}
};
// 获取进度条样式
const getProgressStyle = (progress) => ({
width: `${progress}%`,
height: '8px',
borderRadius: '4px',
backgroundColor: progress === 100 ? '#28a745' : '#007bff',
transition: 'width 0.5s ease-in-out'
});
return (
<div className={`${styles.container} ${darkMode ? styles.darkMode : ''}`}>
{/* 英雄区域 - 行内样式 */}
<div style={inlineStyles.heroSection}>
<h1 className={styles.heroTitle}>项目管理系统</h1>
<p className={styles.heroSubtitle}>管理您的所有项目,跟踪进度,提高效率</p>
<button
className={styles.toggleButton}
onClick={toggleDarkMode}
aria-label={darkMode ? '切换到浅色模式' : '切换到深色模式'}
>
{darkMode ? '☀️ 浅色模式' : '🌙 深色模式'}
</button>
</div>
{/* 搜索和过滤 - CSS Modules */}
<div className={styles.controls}>
<input
type="text"
placeholder="搜索项目..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
className={styles.searchInput}
/>
<div className={styles.tabContainer}>
{['all', 'web', 'mobile', 'ai', 'iot', 'blockchain'].map(tab => (
<button
key={tab}
className={`${styles.tabButton} ${activeTab === tab ? styles.activeTab : ''}`}
onClick={() => setActiveTab(tab)}
>
{tab === 'all' ? '全部' : tab}
</button>
))}
</div>
</div>
{/* 项目卡片网格 - 行内样式 */}
<div style={inlineStyles.cardGrid}>
{filteredProjects.length === 0 ? (
<div className={styles.noResults}>
<p>没有找到匹配的项目</p>
<button
className={styles.resetButton}
onClick={() => {
setSearchTerm('');
setActiveTab('all');
}}
>
重置筛选条件
</button>
</div>
) : (
filteredProjects.map(project => (
<div
key={project.id}
className={`${styles.card} ${animatedCards.includes(project.id) ? styles.animateIn : ''} ${getStatusClass(project.status)}`}
>
<div className={styles.cardHeader}>
<h3 className={styles.cardTitle}>{project.title}</h3>
<span className={`${styles.statusBadge} ${styles[project.status]}`}>
{project.status === 'completed' && '已完成'}
{project.status === 'in-progress' && '进行中'}
{project.status === 'planning' && '规划中'}
</span>
</div>
<div className={styles.cardBody}>
<p className={styles.category}>
<strong>类别:</strong> {project.category}
</p>
<div className={styles.progressContainer}>
<div className={styles.progressLabel}>
<span>进度</span>
<span>{project.progress}%</span>
</div>
<div className={styles.progressBarBackground}>
<div style={getProgressStyle(project.progress)}></div>
</div>
</div>
</div>
<div className={styles.cardFooter}>
<button className={styles.actionButton}>查看详情</button>
<button className={styles.secondaryButton}>编辑</button>
</div>
</div>
))
)}
</div>
{/* 统计信息 - CSS */}
<div className="stats-container">
<div className="stat-card">
<h4>总项目数</h4>
<p className="stat-number">{projects.length}</p>
</div>
<div className="stat-card">
<h4>已完成</h4>
<p className="stat-number">{projects.filter(p => p.status === 'completed').length}</p>
</div>
<div className="stat-card">
<h4>进行中</h4>
<p className="stat-number">{projects.filter(p => p.status === 'in-progress').length}</p>
</div>
<div className="stat-card">
<h4>平均进度</h4>
<p className="stat-number">
{Math.round(projects.reduce((sum, p) => sum + p.progress, 0) / projects.length)}%
</p>
</div>
</div>
</div>
);
}
export default ComprehensiveExample;
对应的 ComprehensiveExample.css
:
/* ComprehensiveExample.css */
.stats-container {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
margin: 40px 0;
}
.stat-card {
background: white;
padding: 20px;
border-radius: 8px;
text-align: center;
box-shadow: 0 2px 10px rgba(0,0,0,0.05);
transition: all 0.3s ease;
}
.stat-card:hover {
transform: translateY(-5px);
box-shadow: 0 5px 20px rgba(0,0,0,0.1);
}
.stat-card h4 {
color: #666;
font-size: 16px;
margin-bottom: 10px;
}
.stat-number {
font-size: 32px;
font-weight: bold;
color: #333;
}
/* 响应式设计 */
@media (max-width: 768px) {
.stats-container {
grid-template-columns: 1fr;
}
.stat-card {
padding: 15px;
}
.stat-number {
font-size: 24px;
}
}
对应的 ComprehensiveExample.module.css
:
/* ComprehensiveExample.module.css */
.container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
font-family: 'Arial', sans-serif;
}
.container.darkMode {
background-color: #1a1a1a;
color: #f0f0f0;
}
.heroTitle {
font-size: 2.5rem;
margin: 0 0 15px 0;
font-weight: 700;
text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
}
.heroSubtitle {
font-size: 1.2rem;
margin: 0 0 30px 0;
opacity: 0.9;
}
.toggleButton {
background: rgba(255,255,255,0.2);
border: 2px solid rgba(255,255,255,0.3);
color: white;
padding: 12px 24px;
border-radius: 30px;
font-size: 16px;
cursor: pointer;
transition: all 0.3s ease;
backdrop-filter: blur(10px);
}
.toggleButton:hover {
background: rgba(255,255,255,0.3);
transform: translateY(-2px);
}
.controls {
display: flex;
flex-wrap: wrap;
gap: 20px;
margin: 30px 0;
padding: 20px;
background: white;
border-radius: 12px;
box-shadow: 0 2px 15px rgba(0,0,0,0.05);
}
.searchInput {
flex: 1;
min-width: 250px;
padding: 12px 16px;
border: 2px solid #e0e0e0;
border-radius: 8px;
font-size: 16px;
transition: border-color 0.3s ease;
}
.searchInput:focus {
outline: none;
border-color: #007bff;
box-shadow: 0 0 0 3px rgba(0,38,155,0.1);
}
.tabContainer {
display: flex;
flex-wrap: wrap;
gap: 10px;
}
.tabButton {
padding: 8px 16px;
background: #f0f0f0;
border: none;
border-radius: 20px;
cursor: pointer;
font-size: 14px;
font-weight: 500;
transition: all 0.3s ease;
}
.tabButton:hover {
background: #e0e0e0;
}
.tabButton.activeTab {
background: #007bff;
color: white;
}
.tabButton.activeTab:hover {
background: #0056b3;
}
.card {
background: white;
border-radius: 12px;
overflow: hidden;
box-shadow: 0 4px 20px rgba(0,0,0,0.08);
transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
opacity: 0;
transform: translateY(30px);
}
.card.animateIn {
opacity: 1;
transform: translateY(0);
}
.card:hover {
transform: translateY(-10px) scale(1.02);
box-shadow: 0 10px 30px rgba(0,0,0,0.15);
}
.cardHeader {
padding: 20px;
border-bottom: 1px solid #eee;
display: flex;
justify-content: space-between;
align-items: center;
}
.cardTitle {
margin: 0;
font-size: 1.3rem;
color: #333;
font-weight: 600;
}
.statusBadge {
padding: 6px 12px;
border-radius: 20px;
font-size: 12px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.completed {
background: #d4edda;
color: #155724;
border-left: 4px solid #28a745;
}
.inProgress {
background: #cce5ff;
color: #004085;
border-left: 4px solid #007bff;
}
.planning {
background: #fff3cd;
color: #856404;
border-left: 4px solid #ffc107;
}
.cardBody {
padding: 20px;
}
.category {
color: #666;
margin: 0 0 20px 0;
font-size: 14px;
}
.progressContainer {
margin: 20px 0;
}
.progressLabel {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 8px;
font-size: 14px;
color: #555;
}
.progressBarBackground {
width: 100%;
height: 8px;
background: #f0f0f0;
border-radius: 4px;
overflow: hidden;
}
.cardFooter {
padding: 20px;
display: flex;
gap: 10px;
border-top: 1px solid #eee;
}
.actionButton {
flex: 1;
padding: 10px;
background: #007bff;
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
font-weight: 500;
transition: all 0.3s ease;
}
.actionButton:hover {
background: #0056b3;
transform: translateY(-2px);
}
.secondaryButton {
padding: 10px 15px;
background: #f0f0f0;
color: #555;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
font-weight: 500;
transition: all 0.3s ease;
}
.secondaryButton:hover {
background: #e0e0e0;
transform: translateY(-2px);
}
.noResults {
grid-column: 1 / -1;
text-align: center;
padding: 60px 20px;
background: #f8f9fa;
border-radius: 12px;
border: 2px dashed #dee2e6;
}
.noResults p {
font-size: 1.2rem;
color: #666;
margin: 0 0 20px 0;
}
.resetButton {
background: #007bff;
color: white;
border: none;
padding: 12px 24px;
border-radius: 6px;
cursor: pointer;
font-size: 16px;
font-weight: 500;
transition: all 0.3s ease;
}
.resetButton:hover {
background: #0056b3;
transform: translateY(-2px);
}
/* 暗色模式样式 */
.container.darkMode .controls {
background: #2a2a2a;
box-shadow: 0 2px 15px rgba(0,0,0,0.2);
}
.container.darkMode .searchInput {
background: #333;
color: #fff;
border-color: #444;
}
.container.darkMode .searchInput:focus {
border-color: #007bff;
box-shadow: 0 0 0 3px rgba(0,123,255,0.2);
}
.container.darkMode .tabButton {
background: #333;
color: #ccc;
}
.container.darkMode .tabButton:hover {
background: #444;
}
.container.darkMode .tabButton.activeTab {
background: #007bff;
color: white;
}
.container.darkMode .card {
background: #2a2a2a;
box-shadow: 0 4px 20px rgba(0,0,0,0.2);
}
.container.darkMode .cardHeader {
border-bottom-color: #444;
}
.container.darkMode .cardTitle {
color: #fff;
}
.container.darkMode .category {
color: #aaa;
}
.container.darkMode .progressLabel {
color: #ccc;
}
.container.darkMode .progressBarBackground {
background: #444;
}
.container.darkMode .cardFooter {
border-top-color: #444;
}
.container.darkMode .secondaryButton {
background: #333;
color: #ccc;
}
.container.darkMode .secondaryButton:hover {
background: #444;
}
/* 响应式设计 */
@media (max-width: 768px) {
.container {
padding: 15px;
}
.heroTitle {
font-size: 2rem;
}
.controls {
flex-direction: column;
padding: 15px;
}
.searchInput {
min-width: auto;
}
.tabContainer {
justify-content: center;
}
.cardHeader {
flex-direction: column;
gap: 10px;
text-align: center;
}
.cardFooter {
flex-direction: column;
}
.actionButton, .secondaryButton {
width: 100%;
}
}
三、总结与最佳实践
1. 选择合适的样式方法
- 小型项目/组件: 行内样式或CSS样式表
- 中大型项目: CSS Modules或Styled-components
- 团队协作: CSS Modules(避免命名冲突)
- 设计系统: Styled-components(主题支持好)
- 快速原型: Tailwind CSS
2. 最佳实践
- 命名规范: 使用有意义的类名,遵循BEM或类似规范
- 组件化: 每个组件有自己的样式,避免全局污染
- 性能考虑: 避免过度使用行内样式,特别是大型列表
- 可维护性: 将样式与逻辑分离,使用CSS变量或主题
- 响应式设计: 始终考虑不同屏幕尺寸的适配
3. 常见问题解决
- 样式不生效: 检查className拼写、CSS文件是否正确导入
- 样式冲突: 使用CSS Modules或更具体的类名
- 性能问题: 避免在render中创建样式对象,使用useMemo
- 动态样式: 使用状态管理,避免直接操作DOM
4. 学习建议
- 从CSS样式表开始,熟悉基础
- 学习CSS Modules,理解局部作用域
- 尝试Styled-components,体验CSS-in-JS
- 掌握Tailwind CSS,提高开发效率
- 学习Sass/SCSS,增强CSS能力
通过以上全面的介绍和详细的案例代码,你应该能够掌握React中各种样式方法的使用。记住,选择适合自己项目和团队的样式方案最重要,不必追求最新最酷的技术,而要注重可维护性和团队协作效率。
更多推荐
所有评论(0)