跳到内容
+

API 设计方法

我们已经深入了解了 Material UI 的使用方式,并且 v1 的重写使我们能够完全重新思考组件 API。

API 设计很难,因为你可以让它看起来简单,但实际上它可能具有迷惑性的复杂性,或者让它实际上很简单,但看起来很复杂。 @sebmarkbage

正如 Sebastian Markbage 指出的那样,没有抽象比错误的抽象更优越。我们正在提供低级组件,以最大限度地提高组合能力。

组合

您可能已经注意到 API 在组件组合方面存在一些不一致性。为了提供一些透明度,我们在设计 API 时一直遵循以下规则

  1. 使用 children prop 是使用 React 进行组合的惯用方式。
  2. 有时我们只需要有限的子组件组合,例如,当我们不需要允许子组件顺序排列时。在这种情况下,提供显式 prop 可以使实现更简单、性能更高;例如,Tab 组件接受 iconlabel prop。
  3. API 一致性很重要。

规则

除了上述组合权衡之外,我们还强制执行以下规则

展开

传递给组件但未明确记录的 props 将会被展开到根元素;例如,className prop 将应用于根元素。

现在,假设您想要禁用 MenuItem 上的涟漪效果。您可以利用展开行为

<MenuItem disableRipple />

disableRipple prop 将按以下方式传递:MenuItem > ListItem > ButtonBase

原生属性

我们避免记录 DOM 支持的原生属性,例如 className

CSS 类名

所有组件都接受 classes prop 来定制样式。类名设计旨在满足两个约束:使类名结构尽可能简单,同时足以实现 Material Design 指南。

  • 应用于根元素的类名始终称为 root
  • 所有默认样式都分组在一个类名中。
  • 应用于非根元素的类名以元素名称为前缀,例如 Dialog 组件中的 paperWidthXs
  • 由布尔值 prop 应用的变体**不**带前缀,例如由 rounded prop 应用的 rounded 类名。
  • 由枚举 prop 应用的变体**带**前缀,例如由 color="primary" prop 应用的 colorPrimary 类名。
  • 一个变体具有**一级特异性**。colorvariant prop 被视为一个变体。样式特异性越低,就越容易覆盖。
  • 我们增加了变体修饰符的特异性。对于伪类(:hover:focus 等),我们**已经必须这样做**。它允许更多的控制,但代价是更多的样板代码。希望它也更直观。
const styles = {
  root: {
    color: green[600],
    '&$checked': {
      color: green[500],
    },
  },
  checked: {},
};

嵌套组件

组件内部的嵌套组件具有

  • 它们自己的扁平化 props,当这些 props 对于顶层组件抽象至关重要时,例如 Input 组件的 id prop。
  • 它们自己的 xxxProps prop,当用户可能需要调整内部 render 方法的子组件时,例如,在使用 Input 组件的组件上公开 inputPropsInputProps prop。
  • 它们自己的 xxxComponent prop,用于执行组件注入。
  • 它们自己的 xxxRef prop,当您可能需要执行命令式操作时,例如,公开 inputRef prop 以访问 Input 组件上的原生 input。这有助于回答“如何访问 DOM 元素?”的问题。

Prop 命名

  • 布尔值

    • 布尔值 prop 的默认值应为 false。这允许更好的简写表示法。考虑一个默认启用的输入框示例。应该如何命名控制此状态的 prop?它应该被称为 disabled

      <Input enabled={false} /><Input disabled />
      
    • 如果布尔值的名称是单个词,它应该是形容词或名词,而不是动词。这是因为 props 描述的是**状态**而不是**动作**。例如,输入框 prop 可以由状态控制,而状态不会用动词来描述

      const [disabled, setDisabled] = React.useState(false);<Input disable={disabled} /><Input disabled={disabled} />
      

受控组件

大多数受控组件由 valueonChange prop 控制。open / onClose / onOpen 组合也用于显示相关状态。在事件较多的情况下,名词在前,动词在后——例如:onPageChangeonRowsChange

布尔值 vs. 枚举

有两种选项可以为组件的变体设计 API:使用布尔值或使用枚举。例如,让我们以具有不同类型的按钮为例。每个选项都有其优点和缺点

  • 选项 1 布尔值

    type Props = {
      contained: boolean;
      fab: boolean;
    };
    

    此 API 启用简写表示法:<Button><Button contained /><Button fab />

  • 选项 2 枚举

    type Props = {
      variant: 'text' | 'contained' | 'fab';
    };
    

    此 API 更加冗长:<Button><Button variant="contained"><Button variant="fab">

    然而,它可以防止使用无效的组合,限制暴露的 props 数量,并且可以轻松地在未来支持新值。

Material UI 组件根据以下规则结合使用了这两种方法

  • 当需要 2 个可能的值时,使用**布尔值**。
  • 当需要 **> 2** 个可能的值时,或者如果将来可能需要额外的可能值时,使用**枚举**。

回到之前的按钮示例;由于它需要 3 个可能的值,我们使用枚举。

Ref

ref 被转发到根元素。这意味着,在不通过 component prop 更改渲染的根元素的情况下,它会被转发到组件渲染的最外层 DOM 元素。如果您通过 component prop 传递不同的组件,则 ref 将附加到该组件。

术语表

  • host componentreact-dom 上下文中的 DOM 节点类型,例如 'div'。另请参阅 React Implementation Notes
  • host elementreact-dom 上下文中的 DOM 节点,例如 window.HTMLDivElement 的实例。
  • outermost:从上到下读取组件树时的第一个组件,即广度优先搜索。
  • root component:渲染 host component 的最外层组件。
  • root element:渲染 host component 的最外层元素。