You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在React UI库中通过Props结合SCSS为组件设置样式

基于React+Storybook+TypeScript+Rollup的UI组件库样式控制方案

背景概述

我用React + Storybook + TypeScript + Rollup构建了一个UI组件库,要求前端不可修改组件代码,所有组件变体必须在库内定义,仅通过传递props使用。为追求高性能、低冗余,不使用styled-components、Material UI、Bootstrap这类UI库。

前端使用示例

<Button size='small' color='primary'> Test </Button>

当前Button组件代码

import React, { MouseEventHandler } from 'react';
import './Button.scss'

export interface ButtonProps {
    children: React.ReactNode,
    color: 'primary' | 'secondary',
    disabled?: boolean,
    size?: 'small' | 'large',
    onClick?: MouseEventHandler<HTMLButtonElement>
}

const Button: React.FC<ButtonProps> = ({size, color, disabled, children, onClick, ...props}) => {  

    return (
        <button type="button">
            {children} 
        </button>
    )
} 

export default Button

1. 不使用styled-components,通过props控制组件样式(SCSS实现)

核心思路是根据props动态生成CSS类名,在SCSS中针对不同类名编写样式变体。

步骤1:组件中拼接类名

修改Button组件,根据传入的size、color、disabled属性生成对应的类名:

const Button: React.FC<ButtonProps> = ({size = 'large', color = 'primary', disabled = false, children, onClick, ...props}) => {  
    // 基础类名 + 变体类名,过滤空值后拼接
    const className = [
        'ui-button',
        size ? `ui-button--size-${size}` : '',
        color ? `ui-button--color-${color}` : '',
        disabled ? 'ui-button--disabled' : ''
    ].filter(Boolean).join(' ');

    return (
        <button 
            type="button"
            className={className}
            disabled={disabled}
            onClick={onClick}
            {...props}
        >
            {children} 
        </button>
    )
} 

步骤2:SCSS中编写样式变体

采用BEM命名规范编写Button.scss,避免样式冲突:

// 基础样式
.ui-button {
    border: none;
    border-radius: 4px;
    cursor: pointer;
    padding: 8px 16px;
    transition: background-color 0.2s ease;

    // 尺寸变体
    &--size-small {
        padding: 4px 8px;
        font-size: 14px;
    }

    &--size-large {
        padding: 12px 24px;
        font-size: 18px;
    }

    // 颜色变体
    &--color-primary {
        background-color: #165DFF;
        color: #FFFFFF;

        &:hover:not(&--disabled) {
            background-color: #0D47A1;
        }
    }

    &--color-secondary {
        background-color: #F5F7FA;
        color: #1D2129;
        border: 1px solid #E5E6EB;

        &:hover:not(&--disabled) {
            background-color: #E5E6EB;
        }
    }

    // 禁用状态
    &--disabled {
        opacity: 0.6;
        cursor: not-allowed;
    }
}

2. TypeScript枚举的合理性及实现

合理性分析

使用TS枚举非常合理,核心优势:

  • 统一管理变体值,避免拼写错误(比如把small写成sm)
  • 提升IDE代码提示性,自动补全枚举值
  • 便于后续扩展(比如新增medium尺寸)

实现方法

  1. 在组件目录下创建Button.enums.ts(或统一放在src/enums/目录):
// Button.enums.ts
export enum ButtonSize {
    Small = 'small',
    Large = 'large'
}

export enum ButtonColor {
    Primary = 'primary',
    Secondary = 'secondary'
}
  1. 修改ButtonProps接口,用枚举替代字面量类型:
import { ButtonSize, ButtonColor } from './Button.enums';

export interface ButtonProps {
    children: React.ReactNode,
    color: ButtonColor,
    disabled?: boolean,
    size?: ButtonSize,
    onClick?: MouseEventHandler<HTMLButtonElement>
}
  1. 前端使用时直接导入枚举:
import { Button, ButtonSize, ButtonColor } from 'your-ui-library';

<Button size={ButtonSize.Small} color={ButtonColor.Primary}> Test </Button>

3. SCSS的最佳结构设计

结合你的项目结构(src/下含components/、stories/,components/下按组件分目录),推荐以下SCSS结构:

src/
├── styles/                # 全局样式目录
│   ├── variables.scss     # 全局变量(颜色、字号、间距等)
│   ├── reset.scss         # 全局样式重置
│   └── mixins.scss        # 全局混合宏(响应式、清除浮动等)
└── components/
    ├── Button/
    │   ├── Button.tsx
    │   ├── Button.scss    # 组件专属样式
    │   └── Button.enums.ts
    └── ...其他组件

结构说明

  • 全局样式层:

    • variables.scss:存放所有全局共享变量,比如主题色、字号基准值
    • reset.scss:统一浏览器默认样式,比如设置box-sizing: border-box、清除默认margin/padding
    • mixins.scss:封装可复用SCSS逻辑,比如@mixin responsive($breakpoint)实现响应式布局
  • 组件样式层:

    • 每个组件的SCSS文件仅负责该组件样式,通过BEM命名避免冲突
    • 组件样式中导入全局变量,保持样式统一:
      // Button.scss
      @import '../../styles/variables.scss';
      
      .ui-button {
          &--color-primary {
              background-color: $color-primary;
              color: $color-white;
          }
      }
      

4. 创建包含全局变量的全局样式

步骤1:定义全局变量

src/styles/variables.scss:

// 主题颜色
$color-primary: #165DFF;
$color-secondary: #F5F7FA;
$color-white: #FFFFFF;
$color-text: #1D2129;

// 字号系统
$font-size-xs: 12px;
$font-size-sm: 14px;
$font-size-base: 16px;
$font-size-lg: 18px;
$font-size-xl: 20px;

// 间距基准
$spacing-xs: 4px;
$spacing-sm: 8px;
$spacing-base: 16px;
$spacing-lg: 24px;
$spacing-xl: 32px;

步骤2:全局样式注入

  1. 创建src/styles/index.scss,导入所有全局样式:
@import './reset.scss';
@import './variables.scss';
@import './mixins.scss';
  1. 在库的入口文件(比如src/index.ts)中导入全局样式:
import './styles/index.scss';
export { default as Button } from './components/Button/Button';
// 导出其他组件...
  1. 前端使用时,用户导入库的入口文件即可自动加载全局样式;也可单独引入:
import 'your-ui-library/dist/styles/index.css';

内容的提问来源于stack exchange,提问作者Luck dev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 11:03:15