React 样式处理
概述
在 React 中有多种方式来处理样式,包括内联样式、CSS 类、CSS 模块、CSS-in-JS 等。本章将学习各种样式处理方法的优缺点和使用场景。
🎨 内联样式
基础内联样式
jsx
function InlineStylesExample() {
const [isActive, setIsActive] = React.useState(false);
// 静态样式对象
const buttonStyle = {
padding: '12px 24px',
border: 'none',
borderRadius: '6px',
cursor: 'pointer',
transition: 'all 0.3s ease',
fontSize: '16px',
fontWeight: 'bold'
};
// 动态样式
const dynamicStyle = {
...buttonStyle,
backgroundColor: isActive ? '#28a745' : '#007bff',
color: 'white',
transform: isActive ? 'scale(1.05)' : 'scale(1)'
};
return (
<div style={{ padding: '20px', fontFamily: 'Arial, sans-serif' }}>
<h2 style={{ color: '#333', marginBottom: '20px' }}>内联样式演示</h2>
<div style={{ marginBottom: '20px' }}>
<button
style={dynamicStyle}
onClick={() => setIsActive(!isActive)}
>
{isActive ? '已激活' : '点击激活'}
</button>
</div>
<div style={{
padding: '16px',
backgroundColor: '#f8f9fa',
border: '1px solid #e9ecef',
borderRadius: '8px',
borderLeft: `4px solid ${isActive ? '#28a745' : '#6c757d'}`
}}>
<h3 style={{ margin: '0 0 10px 0', color: '#495057' }}>状态信息</h3>
<p style={{ margin: 0, color: '#6c757d' }}>
当前状态: {isActive ? '激活' : '未激活'}
</p>
</div>
</div>
);
}响应式内联样式
jsx
function ResponsiveInlineStyles() {
const [windowWidth, setWindowWidth] = React.useState(window.innerWidth);
React.useEffect(() => {
const handleResize = () => setWindowWidth(window.innerWidth);
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
const getResponsiveStyles = () => {
const isMobile = windowWidth < 768;
return {
container: {
padding: isMobile ? '10px' : '20px',
maxWidth: isMobile ? '100%' : '800px',
margin: '0 auto'
},
grid: {
display: 'grid',
gridTemplateColumns: isMobile ? '1fr' : 'repeat(3, 1fr)',
gap: isMobile ? '10px' : '20px'
},
card: {
padding: isMobile ? '12px' : '20px',
backgroundColor: '#fff',
borderRadius: '8px',
boxShadow: '0 2px 4px rgba(0,0,0,0.1)',
border: '1px solid #e9ecef'
}
};
};
const styles = getResponsiveStyles();
return (
<div style={styles.container}>
<h2>响应式内联样式 (宽度: {windowWidth}px)</h2>
<div style={styles.grid}>
{[1, 2, 3].map(num => (
<div key={num} style={styles.card}>
<h3>卡片 {num}</h3>
<p>这是一个响应式卡片,会根据屏幕宽度调整布局。</p>
</div>
))}
</div>
</div>
);
}📁 CSS 类和模块
传统 CSS 类
jsx
// 需要对应的 CSS 文件
function TraditionalCSS() {
const [theme, setTheme] = React.useState('light');
return (
<div className={`app-container ${theme}-theme`}>
<header className="app-header">
<h1 className="app-title">传统 CSS 演示</h1>
<button
className="theme-toggle-btn"
onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}
>
切换主题
</button>
</header>
<main className="app-main">
<div className="card-grid">
{['primary', 'success', 'warning', 'danger'].map(variant => (
<div key={variant} className={`card card-${variant}`}>
<h3 className="card-title">{variant} 卡片</h3>
<p className="card-content">这是一个 {variant} 样式的卡片。</p>
<button className={`btn btn-${variant}`}>操作</button>
</div>
))}
</div>
</main>
</div>
);
}CSS 模块
jsx
// 使用 CSS 模块(需要配置支持)
// styles.module.css
/*
.container {
padding: 20px;
max-width: 1200px;
margin: 0 auto;
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 30px;
padding-bottom: 20px;
border-bottom: 2px solid #e9ecef;
}
.title {
color: #343a40;
font-size: 2rem;
margin: 0;
}
.button {
padding: 10px 20px;
border: none;
border-radius: 6px;
cursor: pointer;
font-weight: bold;
transition: all 0.3s ease;
}
.primary {
background-color: #007bff;
color: white;
}
.primary:hover {
background-color: #0056b3;
}
*/
// 模拟 CSS 模块对象
const styles = {
container: 'styles_container__3Xj2k',
header: 'styles_header__2Pm9L',
title: 'styles_title__1Kc8n',
button: 'styles_button__4Nh7m',
primary: 'styles_primary__8Qx1p'
};
function CSSModulesExample() {
return (
<div className={styles.container}>
<header className={styles.header}>
<h1 className={styles.title}>CSS 模块演示</h1>
<button className={`${styles.button} ${styles.primary}`}>
CSS 模块按钮
</button>
</header>
<div>
<p>CSS 模块提供了局部作用域的 CSS,避免样式冲突。</p>
<p>类名会被自动哈希化,确保唯一性。</p>
</div>
</div>
);
}🎭 CSS-in-JS
Styled Components 风格
jsx
// 模拟 styled-components API
function createStyledComponent(tag, styles) {
return function StyledComponent({ children, ...props }) {
const computedStyles = typeof styles === 'function' ? styles(props) : styles;
return React.createElement(tag, { style: computedStyles, ...props }, children);
};
}
// 创建样式化组件
const StyledContainer = createStyledComponent('div', {
padding: '20px',
maxWidth: '800px',
margin: '0 auto',
fontFamily: 'Arial, sans-serif'
});
const StyledButton = createStyledComponent('button', (props) => ({
padding: '12px 24px',
border: 'none',
borderRadius: '6px',
cursor: 'pointer',
fontSize: '16px',
fontWeight: 'bold',
transition: 'all 0.3s ease',
backgroundColor: props.variant === 'primary' ? '#007bff' : '#6c757d',
color: 'white',
opacity: props.disabled ? 0.6 : 1,
transform: props.active ? 'scale(0.95)' : 'scale(1)'
}));
const StyledCard = createStyledComponent('div', (props) => ({
padding: '20px',
backgroundColor: '#fff',
borderRadius: '8px',
boxShadow: '0 2px 10px rgba(0,0,0,0.1)',
border: `2px solid ${props.borderColor || '#e9ecef'}`,
transition: 'all 0.3s ease',
':hover': {
transform: 'translateY(-2px)',
boxShadow: '0 4px 20px rgba(0,0,0,0.15)'
}
}));
function StyledComponentsExample() {
const [activeCard, setActiveCard] = React.useState(null);
const cards = [
{ id: 1, title: '卡片 1', color: '#007bff' },
{ id: 2, title: '卡片 2', color: '#28a745' },
{ id: 3, title: '卡片 3', color: '#ffc107' }
];
return (
<StyledContainer>
<h2>CSS-in-JS 演示</h2>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(200px, 1fr))', gap: '20px', marginBottom: '20px' }}>
{cards.map(card => (
<StyledCard
key={card.id}
borderColor={activeCard === card.id ? card.color : '#e9ecef'}
onClick={() => setActiveCard(activeCard === card.id ? null : card.id)}
style={{ cursor: 'pointer' }}
>
<h3 style={{ margin: '0 0 10px 0', color: card.color }}>{card.title}</h3>
<p style={{ margin: 0, color: '#6c757d' }}>
点击选择此卡片
</p>
</StyledCard>
))}
</div>
<div style={{ textAlign: 'center' }}>
<StyledButton variant="primary" style={{ marginRight: '10px' }}>
主要按钮
</StyledButton>
<StyledButton disabled>
禁用按钮
</StyledButton>
</div>
</StyledContainer>
);
}🎨 动态样式主题
主题系统
jsx
// 主题配置
const themes = {
light: {
primary: '#007bff',
secondary: '#6c757d',
success: '#28a745',
danger: '#dc3545',
warning: '#ffc107',
info: '#17a2b8',
background: '#ffffff',
surface: '#f8f9fa',
text: '#212529',
textSecondary: '#6c757d',
border: '#e9ecef'
},
dark: {
primary: '#0d6efd',
secondary: '#6c757d',
success: '#198754',
danger: '#dc3545',
warning: '#ffc107',
info: '#0dcaf0',
background: '#212529',
surface: '#343a40',
text: '#ffffff',
textSecondary: '#adb5bd',
border: '#495057'
}
};
// 主题上下文
const ThemeContext = React.createContext();
function ThemeProvider({ children }) {
const [currentTheme, setCurrentTheme] = React.useState('light');
const theme = themes[currentTheme];
const toggleTheme = () => {
setCurrentTheme(prev => prev === 'light' ? 'dark' : 'light');
};
return (
<ThemeContext.Provider value={{ theme, currentTheme, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
}
function useTheme() {
const context = React.useContext(ThemeContext);
if (!context) {
throw new Error('useTheme must be used within ThemeProvider');
}
return context;
}
// 主题化组件
function ThemedComponent() {
const { theme, toggleTheme, currentTheme } = useTheme();
const containerStyle = {
minHeight: '400px',
backgroundColor: theme.background,
color: theme.text,
padding: '20px',
transition: 'all 0.3s ease'
};
const cardStyle = {
backgroundColor: theme.surface,
border: `1px solid ${theme.border}`,
borderRadius: '8px',
padding: '20px',
marginBottom: '20px'
};
const buttonStyle = (variant = 'primary') => ({
backgroundColor: theme[variant],
color: variant === 'warning' ? '#000' : '#fff',
border: 'none',
padding: '10px 20px',
borderRadius: '6px',
cursor: 'pointer',
margin: '5px',
fontWeight: 'bold'
});
return (
<div style={containerStyle}>
<div style={cardStyle}>
<h2 style={{ color: theme.text, marginTop: 0 }}>主题系统演示</h2>
<p style={{ color: theme.textSecondary }}>
当前主题: {currentTheme}
</p>
<button style={buttonStyle('primary')} onClick={toggleTheme}>
{currentTheme === 'light' ? '🌙' : '☀️'} 切换主题
</button>
</div>
<div style={cardStyle}>
<h3 style={{ color: theme.text }}>按钮变体</h3>
<div>
{['primary', 'secondary', 'success', 'danger', 'warning', 'info'].map(variant => (
<button key={variant} style={buttonStyle(variant)}>
{variant}
</button>
))}
</div>
</div>
<div style={cardStyle}>
<h3 style={{ color: theme.text }}>颜色展示</h3>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(120px, 1fr))', gap: '10px' }}>
{Object.entries(theme).map(([key, value]) => (
<div key={key} style={{
backgroundColor: value,
color: ['background', 'surface'].includes(key) ? theme.text : '#fff',
padding: '10px',
borderRadius: '4px',
textAlign: 'center',
fontSize: '12px',
border: `1px solid ${theme.border}`
}}>
{key}
</div>
))}
</div>
</div>
</div>
);
}
function ThemeExample() {
return (
<ThemeProvider>
<ThemedComponent />
</ThemeProvider>
);
}📝 本章小结
通过本章学习,你应该掌握了:
样式处理方法
- ✅ 内联样式:适合动态样式
- ✅ CSS 类:传统方法,简单有效
- ✅ CSS 模块:局部作用域,避免冲突
- ✅ CSS-in-JS:组件级样式,动态主题
最佳实践
- 选择合适的方法:根据项目需求选择样式方案
- 性能考虑:避免频繁的样式计算
- 主题化设计:支持多主题切换
- 响应式设计:适配不同屏幕尺寸
- 代码组织:保持样式代码的可维护性
继续学习:下一章 - React 路由