React中的 css in js的使用示例

CssInJs.tsx

javascript 复制代码
import React, { useState } from 'react'
import styled from 'styled-components'

// 定义主题类型接口 - 规范主题配色方案的类型
interface Theme {
  primaryColor: string    // 主色调
  secondaryColor: string  // 辅助色
  dangerColor: string     // 危险/错误状态色
  backgroundColor: string // 背景色
  textColor: string       // 文本色
}

// 创建默认主题配置 - 统一应用的色彩风格
const theme: Theme = {
  primaryColor: '#007bff',    // 蓝色主调
  secondaryColor: '#6c757d',  // 灰色辅助色
  dangerColor: '#dc3545',     // 红色危险色
  backgroundColor: '#f8f9fa', // 浅灰背景
  textColor: '#212529'        // 深灰文本
}

// 按钮组件Props类型接口 - 定义按钮的可配置属性
interface ButtonProps {
  variant?: 'primary' | 'secondary' | 'danger' // 按钮样式变体
  size?: 'small' | 'medium' | 'large'          // 按钮尺寸
  active?: boolean                             // 是否激活状态
  disabled?: boolean                            // 是否禁用状态
}

// 卡片组件Props类型接口 - 定义卡片的可配置属性
interface CardProps {
  elevation?: number // 卡片阴影层级(控制立体感)
}

// 容器组件 - 应用主容器,控制全局布局和背景
const Container = styled.div`
  padding: 20px;
  background-color: ${theme.backgroundColor}; // 引用主题背景色
  min-height: 100vh; // 占满整个视口高度
`

// 页头组件 - 页面标题样式
const Header = styled.h1`
  color: ${theme.textColor}; // 引用主题文本色
  text-align: center; // 文字居中
  margin-bottom: 30px; // 底部间距
`

// 控制区容器 - 包裹所有交互控制组件,统一布局
const ControlsWrapper = styled.div`
  display: flex; // 弹性布局
  flex-wrap: wrap; // 自动换行(适配小屏幕)
  gap: 15px; // 组件间距
  justify-content: center; // 水平居中
  margin-bottom: 30px; // 底部间距
  padding: 20px; // 内边距
  background-color: #e9ecef; // 浅灰背景
  border-radius: 8px; // 圆角
`

// 样式化按钮组件 - 支持多变体、多尺寸、状态控制
const StyledButton = styled.button<ButtonProps>`
  // 内边距 - 根据size属性动态调整
  padding: ${props => {
    switch (props.size) {
      case 'small': return '6px 12px' // 小尺寸内边距
      case 'large': return '18px 36px' // 大尺寸内边距
      default: return '12px 24px' // 默认(中)尺寸内边距
    }
  }};
  
  // 字体大小 - 根据size属性动态调整
  font-size: ${props => {
    switch (props.size) {
      case 'small': return '12px' // 小尺寸字体
      case 'large': return '20px' // 大尺寸字体
      default: return '16px' // 默认(中)尺寸字体
    }
  }};
  
  // 背景色 - 优先处理禁用状态,再根据variant属性调整
  background-color: ${props => {
    if (props.disabled) return '#ccc' // 禁用状态灰色
    switch (props.variant) {
      case 'secondary': return theme.secondaryColor // 辅助色
      case 'danger': return theme.dangerColor // 危险色
      default: return theme.primaryColor // 默认主色调
    }
  }};
  
  color: white; // 文字白色
  border: none; // 无边框
  border-radius: 4px; // 圆角
  cursor: ${props => props.disabled ? 'not-allowed' : 'pointer'}; // 鼠标指针样式
  opacity: ${props => props.disabled ? 0.6 : 1}; // 禁用状态透明度降低
  transform: ${props => props.active ? 'scale(1.05)' : 'scale(1)'}; // 激活状态轻微放大
  transition: all 0.3s ease; // 所有属性过渡动画(0.3秒,缓动效果)
  font-weight: bold; // 字体加粗
  
  // hover状态 - 非禁用时添加放大和阴影效果
  &:hover {
    transform: ${props => !props.disabled ? 'scale(1.05)' : 'none'};
    box-shadow: ${props => !props.disabled ? '0 4px 8px rgba(0,0,0,0.2)' : 'none'};
  }
  
  // focus状态 - 聚焦时添加外边框(提升可访问性)
  &:focus {
    outline: 2px solid ${theme.primaryColor};
    outline-offset: 2px;
  }
`

// 卡片组件 - 内容容器,支持阴影层级控制
const Card = styled.div<CardProps>`
  background: white; // 白色背景
  padding: 20px; // 内边距
  border-radius: 8px; // 圆角
  // 阴影效果 - 根据elevation属性动态计算(默认2级)
  box-shadow: ${props => 
    `0 ${props.elevation || 2}px ${props.elevation ? props.elevation * 2 : 4}px rgba(0,0,0,0.1)`};
  margin: 20px auto; // 上下间距,水平居中
  max-width: 600px; // 最大宽度(限制卡片宽度)
  transition: box-shadow 0.3s ease; // 阴影过渡动画
  
  // hover状态 - 阴影加深(提升交互感)
  &:hover {
    box-shadow: ${props => 
      `0 ${Math.min((props.elevation || 2) * 2, 12)}px ${Math.min((props.elevation || 2) * 4, 24)}px rgba(0,0,0,0.15)`};
  }
`

// 卡片标题组件 - 卡片内标题样式
const CardTitle = styled.h2`
  margin-top: 0; // 清除默认上边距
  color: ${theme.textColor}; // 引用主题文本色
  border-bottom: 2px solid ${theme.primaryColor}; // 底部主色调边框
  padding-bottom: 10px; // 底部内边距(与边框间距)
`

// 特性列表组件 - 自定义列表样式
const FeatureList = styled.ul`
  padding-left: 20px; // 左侧内边距(调整列表缩进)
  
  li {
    margin-bottom: 10px; // 列表项底部间距
    line-height: 1.5; // 行高(提升可读性)
  }
  
  // 列表项前缀 - 添加对勾图标
  li::before {
    content: "✓"; // 对勾符号
    color: ${theme.primaryColor}; // 主色调
    font-weight: bold; // 加粗
    display: inline-block; // 行内块级(便于控制宽度)
    width: 1em; // 宽度(与文字对齐)
    margin-left: -1em; // 左移(实现自定义缩进)
  }
`

// 切换开关容器 - 包裹开关和文字,控制布局
const ToggleContainer = styled.div`
  display: flex; // 弹性布局
  align-items: center; // 垂直居中
  gap: 10px; // 组件间距
`

// 切换开关组件 - 自定义开关样式(基于checkbox)
const ToggleSwitch = styled.label<{ checked: boolean }>`
  position: relative; // 相对定位(用于内部绝对定位元素)
  display: inline-block; // 行内块级
  width: 60px; // 开关宽度
  height: 34px; // 开关高度
  
  // 隐藏原生checkbox
  input {
    opacity: 0; // 透明
    width: 0; // 宽度为0
    height: 0; // 高度为0
  }
  
  // 开关背景滑块
  span {
    position: absolute; // 绝对定位(覆盖整个label)
    cursor: pointer; // 鼠标指针(手型)
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background-color: ${props => props.checked ? theme.primaryColor : '#ccc'}; // 选中/未选中背景色
    transition: .4s; // 过渡动画(0.4秒)
    border-radius: 34px; // 圆角(圆形滑块)
    
    // 开关按钮(圆形)
    &::before {
      position: absolute; // 绝对定位
      content: ""; // 空内容(纯样式元素)
      height: 26px; // 按钮高度
      width: 26px; // 按钮宽度
      left: 4px; // 左偏移
      bottom: 4px; // 底偏移
      background-color: white; // 白色背景
      transition: .4s; // 过渡动画
      border-radius: 50%; // 圆形
      transform: ${props => props.checked ? 'translateX(26px)' : 'translateX(0)'}; // 选中时右移
    }
  }
`

// 主组件 - CSS-in-JS演示入口
const CssInJsDemo: React.FC = () => {
  // 状态管理 - 控制按钮和卡片的动态样式
  const [buttonVariant, setButtonVariant] = useState<ButtonProps['variant']>('primary') // 按钮变体
  const [buttonSize, setButtonSize] = useState<ButtonProps['size']>('medium')          // 按钮尺寸
  const [isButtonActive, setIsButtonActive] = useState(false)                          // 按钮激活状态
  const [isButtonDisabled, setIsButtonDisabled] = useState(false)                      // 按钮禁用状态
  const [cardElevation, setCardElevation] = useState(2)                                // 卡片阴影层级
  const [darkMode, setDarkMode] = useState(false)                                      // 深色模式开关

  return (
    <Container>
      <Header>CSS-in-JS 示例 (Styled Components)</Header>
      
      {/* 控制区 - 用于调整组件样式参数 */}
      <ControlsWrapper>
        {/* 切换按钮变体(primary/secondary) */}
        <StyledButton 
          variant={buttonVariant === 'primary' ? 'secondary' : 'primary'} 
          onClick={() => setButtonVariant(buttonVariant === 'primary' ? 'secondary' : 'primary')}
        >
          切换主题: {buttonVariant === 'primary' ? 'Secondary' : 'Primary'}
        </StyledButton>
        
        {/* 切换按钮尺寸(medium/large) */}
        <StyledButton 
          size={buttonSize === 'medium' ? 'large' : 'medium'} 
          onClick={() => setButtonSize(buttonSize === 'medium' ? 'large' : 'medium')}
        >
          切换大小: {buttonSize === 'medium' ? 'Large' : 'Medium'}
        </StyledButton>
        
        {/* 按钮激活状态切换开关 */}
        <ToggleContainer>
          <span>激活状态:</span>
          <ToggleSwitch checked={isButtonActive}>
            <input 
              type="checkbox" 
              checked={isButtonActive}
              onChange={() => setIsButtonActive(!isButtonActive)}
            />
            <span />
          </ToggleSwitch>
        </ToggleContainer>
        
        {/* 按钮禁用状态切换开关 */}
        <ToggleContainer>
          <span>禁用状态:</span>
          <ToggleSwitch checked={isButtonDisabled}>
            <input 
              type="checkbox" 
              checked={isButtonDisabled}
              onChange={() => setIsButtonDisabled(!isButtonDisabled)}
            />
            <span />
          </ToggleSwitch>
        </ToggleContainer>
        
        {/* 调整卡片阴影层级(2-12级,步长2) */}
        <StyledButton 
          onClick={() => setCardElevation(cardElevation >= 12 ? 2 : cardElevation + 2)}
        >
          卡片阴影: {cardElevation} 级
        </StyledButton>
        
        {/* 深色模式切换开关 */}
        <ToggleContainer>
          <span>深色模式:</span>
          <ToggleSwitch checked={darkMode}>
            <input 
              type="checkbox" 
              checked={darkMode}
              onChange={() => setDarkMode(!darkMode)}
            />
            <span />
          </ToggleSwitch>
        </ToggleContainer>
      </ControlsWrapper>
      
      {/* 演示卡片1 - CSS-in-JS特性介绍 */}
      <Card elevation={cardElevation}>
        <CardTitle>关于 CSS-in-JS</CardTitle>
        <FeatureList>
          <li>将CSS直接写在JavaScript/TypeScript组件中</li>
          <li>支持动态样式,可以根据组件状态改变样式</li>
          <li>自动处理浏览器前缀</li>
          <li>避免CSS类名冲突</li>
          <li>支持主题和变量</li>
          <li>具备完整的TypeScript类型支持</li>
          <li>样式与组件紧密耦合,便于维护</li>
        </FeatureList>
        
        {/* 动态样式按钮 - 应用上方控制区的所有状态 */}
        <StyledButton
          variant={buttonVariant}
          size={buttonSize}
          active={isButtonActive}
          disabled={isButtonDisabled}
          onClick={() => alert('按钮被点击了!')}
          // 内联样式 - 深色模式下覆盖背景色和文本色
          style={{ 
            marginTop: '20px',
            backgroundColor: darkMode ? '#343a40' : undefined,
            color: darkMode ? '#f8f9fa' : undefined
          }}
        >
          动态样式按钮
        </StyledButton>
      </Card>
      
      {/* 演示卡片2 - CSS-in-JS使用方法 */}
      <Card elevation={cardElevation}>
        <CardTitle>使用方法</CardTitle>
        <FeatureList>
          <li>使用styled.[element]创建样式化组件</li>
          <li>通过props传递样式参数</li>
          <li>使用模板字符串编写CSS</li>
          <li>在组件中像普通React组件一样使用</li>
          <li>支持伪类、媒体查询等CSS特性</li>
          <li>可以继承和扩展其他样式组件</li>
        </FeatureList>
      </Card>
    </Container>
  )
}

export default CssInJsDemo

vite.config.ts

javascript 复制代码
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

// https://vite.dev/config/
export default defineConfig({
  plugins: [
    react({
      babel: {
        plugins: [
          [
            'babel-plugin-styled-components',
            {
              displayName: true,
              fileName: false
            }
          ]
        ]
      }
    })
  ],
})
相关推荐
随风一样自由1 小时前
React内逐行解释这个 package.json 文件,最近搞了个工厂AI生产平台,顺便来学习一下
学习·react.js·json·package
by__csdn1 小时前
Ajax与Axios终极对比指南全方位对比解析
前端·javascript·ajax·okhttp·typescript·vue·restful
khatung1 小时前
借助Electron打通平台与用户通知(macOS系统)
前端·javascript·vscode·react.js·macos·electron·前端框架
小年糕是糕手1 小时前
【C++同步练习】类和对象(一)
java·开发语言·javascript·数据结构·c++·算法·排序算法
by__csdn1 小时前
Vue3+Axios终极封装指南
前端·javascript·vue.js·http·ajax·typescript·vue
cauyyl1 小时前
react native straoge 切换缓存组件踩坑记录
javascript·react native·react.js
小年糕是糕手1 小时前
【C++同步练习】类和对象(二)
java·开发语言·javascript·数据结构·c++·算法·ecmascript
PitayaDog1 小时前
(一)React19+TS基础进阶与实战完全指南
react.js
fruge1 小时前
从 0 到 1 搭建 Vue3+Vite 工程化项目:含路由、状态管理、按需引入
前端·javascript·vue.js