1. Button 核心属性总览:type、disabled、loading、icon、size

Button(按钮)是组件库中出现频率最高的基础交互元素。一个 Button 实例的行为、外观与状态全部由一组有限且可组合的 API 属性控制。本章将系统拆解 type、disabled、loading、icon、size 五个核心属性的取值边界、作用机制及接入方式。理解这五个属性的语义与协同关系,是后续学习 TextField 及其它表单组件 API 设计的基石。

1.1 属性速查与取值清单

属性名 类型 可选值 默认值 作用简述
type string default primary dashed text link default 定义视觉风格层级
disabled boolean true false false 禁用交互与视觉置灰
loading boolean true false false 显示加载中状态,禁止二次点击
icon ReactNode / string 图标组件名或元素 — 在按钮文字前插入图标
size string large middle small middle 控制按钮高度与内间距
接入直觉: 五个属性中 type 与 size 控制“外观骨架”,disabled 与 loading 控制“交互状态”,icon 负责“信息点缀”。它们彼此正交,即修改一个属性不会意外覆盖另一个属性的语义。

1.2 属性逐项解析

1.2.1 type —— 视觉层级的首要区分器

type 决定了按钮在页面中的视觉权重。五种取值对应不同的填充策略与边框规则:

  • default:白底灰边框,用于次要操作,如“取消”“返回”。
  • primary:品牌色填充,用于页面中最核心的引导操作,如“提交”“确认”。
  • dashed:白底虚线边框,常见于“添加”“上传”等拓展性操作。
  • text:完全无边框无背景,仅保留文字,用于低干扰的链接式操作。
  • link:文字带下划线,外观与超链接一致,用于跳转或触发弹窗。
常见陷阱: 同一页面中 primary 按钮不应超过两个。link 类型的按钮虽然外观像超链接,但它仍然会触发 onClick 事件且保留按钮语义,不要与原生 <a> 标签混用。

1.2.2 disabled —— 硬性阻断所有用户交互

当 disabled=true 时,按钮的 onClick、onMouseEnter、onFocus 等事件全部被框架阻断,同时组件附加一层灰色遮罩或降低不透明度。浏览器默认行为(如键盘 Tab 聚焦)也会被 pointer-events: none 抑制。

// 禁用状态接入示例(React)
<Button
  type="primary"
  disabled={!isFormValid}
  onClick={handleSubmit}
>
  提交订单
</Button>
调试要点: 检查 disabled 是否生效,不要只看视觉灰色,必须确认控制台打印的事件绑定未被触发。推荐在 onClick 回调第一行加 console.log('clicked') 来验证。

1.2.3 loading —— 异步操作的瞬时反馈

loading 状态会替换按钮原有图标或文字左侧为旋转动画,同时自动获得 disabled 效果(防止重复提交)。加载时长通常与后端接口响应时间绑定,前端只需在发起请求时置 true,在 finally 中复位。

// 标准 loading 生命周期管理
const [submitting, setSubmitting] = useState(false);

async function handleConfirm() {
  setSubmitting(true);
  try {
    await api.submitOrder(formData);
  } finally {
    setSubmitting(false);
  }
}

return (
  <Button
    type="primary"
    loading={submitting}
    onClick={handleConfirm}
  >
    确认提交
  </Button>
);

1.2.4 icon —— 图形化语义增强

icon 属性接收一个图标组件实例(如 <SearchOutlined />)或图标名称字符串(取决于组件库实现)。图标默认位于文字左侧,部分组件库支持通过 iconPosition 调整到右侧。当只传递 icon 而不传入子元素时,按钮变为纯图标按钮,此时务必保证 aria-label 属性携带文案以支持无障碍。

// 图标按钮最佳实践
<Button
  type="default"
  icon={<SearchOutlined />}
  aria-label="搜索"
/>

// 带文字图标按钮
<Button
  type="primary"
  icon={<DownloadOutlined />}
>
  导出报表
</Button>

1.2.5 size —— 间距系统的横向对齐

size 控制按钮的高度、内边距(padding)和字号。三种规格与设计系统的间距刻度保持一致:large(高度 40px,字号 16px)、middle(高度 32px,字号 14px)、small(高度 24px,字号 12px)。当按钮与其他表单组件(如 Input、Select)并排时,必须使用同一 size 值来保证基线对齐。

1.3 属性协同关系与初始化流程

下面这张 SVG 结构图展示了五属性在 Button 渲染流水线中的处理顺序:type 最先决定样式类名,size 确定尺寸 ,icon 在渲染开始时插入,loading 覆盖图标并触发 disabled,最后 disabled 做最终阻断。

Button 五属性处理流程 Button 属性处理流水线 type + size + icon + loading + disabled 1️⃣ type → 注入样式类(default/primary/dashed/text/link) 2️⃣ size → 注入高度/间距/字号(large/middle/small) 3️⃣ icon → 渲染左侧图标(若有) 4️⃣ loading → 替换图标为旋转动画 + 隐式 disabled 5️⃣ disabled → 阻断事件 + 灰色遮罩(优先级最高) ⚠ 关键依赖 loading=true 时自动 设置 disabled=true 但 disabled 可独立 设置为 true 而不 触发 loading 动画

1.4 完整接入示例

以下是一个同时使用五个属性的 Button 示例,涵盖初始化状态、异步加载与禁用降级:

import { Button } from 'your-component-library';
import { DownloadOutlined, LoadingOutlined } from '@ant-design/icons';
import { useState } from 'react';

function ExportPanel() {
  const [exporting, setExporting] = useState(false);
  const [hasData] = useState(true); // 模拟数据是否为空

  return (
    <Button
      type="primary"
      size="large"
      icon={exporting ? undefined : <DownloadOutlined />}
      loading={exporting}
      disabled={!hasData}
      onClick={async () => {
        setExporting(true);
        await fetch('/api/export');
        setExporting(false);
      }}
    >
      {exporting ? '导出中…' : '导出数据'}
    </Button>
  );
}

1.5 样式适配要点

属性变更时 Button 的样式响应链路如下:

  • type 切换 → 组件重新计算 --btn-bg、--btn-border、--btn-color 三个 CSS 变量。
  • size 切换 → 组件应用不同的 --btn-height、--btn-padding、--btn-font-size。
  • loading 与 disabled 共用一套 --btn-opacity(通常 0.65)但 loading 保留旋转动画,disabled 移除所有动画。
  • icon 的尺寸跟随 size 自动缩放:large=20px,middle=16px,small=14px。

1.6 常见错误排查清单

错误现象 可能原因 解决方案
按钮颜色没有变化 type 传入了未定义的字符串(如 "success") 检查 type 取值是否在组件库支持的枚举内
点击后仍触发事件但视觉已置灰 disabled 未绑定到组件的 disabled prop,仅修改了样式 显式传入 disabled=true,不要只加 CSS class
loading 状态下按钮可被多次点击 未启用组件库内置的 loading 阻断,或手动覆盖了 pointer-events 确认 loading prop 生效,不要在父容器覆写 pointer-events: auto
图标与文字间距过大/过小 自定义了 gap 或 margin 覆盖了组件默认间距 优先使用 --btn-icon-gap 等 CSS 变量调整,避免暴力覆盖
size=small 时按钮文字溢出 使用了超长文字或字号未同步缩小 控制按钮文案长度,或使用 ellipsis 溢出省略

1.7 性能优化微提示

  • 避免不必要的重渲染: type、size 在初始化后很少改变,应使用 React.memo 包裹 Button 或通过状态提升来减少父组件更新导致的按钮重渲染。
  • 图标懒加载: 如果按钮隐藏在 Tab 或折叠面板中,图标组件可使用动态 React.lazy 加载,减少首屏 JS 体积。
  • loading 动画不要用大 JS 库: 大多数组件库的 loading 动画是纯 CSS 实现,不需要额外引入 spinner 库;检查组件 DOM 结构,确认加载动效仅通过 @keyframes 完成。
  • disabled 状态避免复杂选择器: 框架在 disabled 时通常只添加一个 .btn-disabled 类,CSS 优先级控制在 0-1-1 以内,防止后续覆盖困难。
本章小结: type、disabled、loading、icon、size 构成了 Button 组件最基础的 API 面。掌握这五个属性的取值边界与接入时机,就等于抓住了 80% 的 Button 开发场景。下一属性集合(如 block、ghost、shape、htmlType)均是在这五个属性之上的扩展与修饰。