Skip to content

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:组件级样式,动态主题

最佳实践

  1. 选择合适的方法:根据项目需求选择样式方案
  2. 性能考虑:避免频繁的样式计算
  3. 主题化设计:支持多主题切换
  4. 响应式设计:适配不同屏幕尺寸
  5. 代码组织:保持样式代码的可维护性

继续学习下一章 - React 路由

本站内容仅供学习和研究使用。