跳到内容
+

自定义 MUI Base 组件

自定义 MUI Base 组件有几种方法,从应用自定义 CSS 规则到使用 Hook 构建完全自定义的组件。

使用 MUI Base,您可以自由决定要自定义组件结构和样式的程度。

组件样式化

本节回顾了几种可用的自定义方法:应用自定义 CSS 规则、覆盖默认子组件插槽、自定义插槽 props 以及使用 Hook 构建完全自定义的组件。

选择哪个选项?

多种选项可能会让人感到不知所措,尤其是当您是 MUI Base 的新手时。那么,如何决定使用哪一个呢?

首先要做的决定是使用非样式化组件还是 Hook。Hook 更适合制作可以进一步自定义的组件库。例如,我们自己的 Joy UI 就是使用 MUI Base 的 Hook 实现的。Hook 也作为几个 Material UI 组件的基础,并且该库的未来版本将更广泛地使用它们。

如果您不需要使您的组件库可自定义(例如,通过暴露 slotProps),那么非样式化组件由于其简洁性可能是一个更好的选择。

在选择非样式化组件之后,还需要做一个决定:如何对其进行样式化。答案取决于项目中使用的样式解决方案

Plain CSS, Sass, Less

...或任何其他编译为 CSS 的东西

您可以使用内置类来样式化组件,或者指定您自己的类并在样式表中引用它们。

CSS Modules

当使用 CSS Modules 时,最简单的方法是使用 slotProps 指定自定义类,如下所示

import clsx from 'clsx';
import { Switch as BaseSwitch, SwitchOwnerState } from '@mui/base/Switch';
import classes from './styles.module.css';

export default function Switch(props) {
  const slotProps = {
    root: (ownerState: SwitchOwnerState) => ({
      className: clsx(classes.root, {
        [classes.checked]: ownerState.checked,
        [classes.disabled]: ownerState.disabled,
      }),
    }),
    thumb: { className: classes.thumb },
    track: { className: classes.track },
    input: { className: classes.input },
  };

  return <BaseSwitch {...props} slotProps={slotProps} />;
}

在这个例子中,我们使用 clsx 实用程序来减少有条件地应用类名称所需的工作量。

Tailwind CSS

使用 slotProps 来应用自定义样式,使用 Tailwind CSS,如下所示

import { Switch as BaseSwitch, SwitchOwnerState } from '@mui/base/Switch';

export default function Switch(props) {
  const slotProps = {
    root: (ownerState: SwitchOwnerState) => ({
      className: `inline-block w-8 h-5 rounded-full cursor-pointer relative ${
        ownerState.checked ? 'bg-cyan-500' : 'bg-zinc-400'
      }`,
    }),
    thumb: (ownerState: SwitchOwnerState) => ({
      className: `bg-white block w-3.5 h-3.5 rounded-full relative top-[3px] ${
        ownerState.checked ? 'left-[3px]' : 'left-[14px]'
      }`,
    }),
    input: { className: 'absolute w-full h-full inset-0 opacity-0 z-10 m-0' },
  };

  return <BaseSwitch {...props} slotProps={slotProps} />;
}

有关集成 MUI Base 和 Tailwind CSS 的更多信息,请参阅我们的使用 Tailwind CSS 指南

Styled components

如果您将 CSS-in-JS 解决方案与类似 styled-components 的 API(例如 MUI SystemEmotion)一起使用,最好的方法是使用 slots 属性提供样式化的子组件,如 下面的演示所示。

或者,您可以将整个非样式化组件包装在 styled 实用程序中,并使用 CSS 类定位各个子组件

Enter 开始编辑

应用自定义 CSS 规则

如果您对组件渲染的 HTML 的默认结构感到满意,则可以将自定义样式应用于组件的类。

每个组件都有自己的一组类。有些类是静态的,也就是说它们始终存在于组件上。另一些类是有条件地应用的——例如 base--disabled,它仅在组件被禁用时存在。

每个组件的 API 文档都列出了该组件使用的所有类。此外,您可以导入一个 [componentName]Classes 对象,该对象描述给定组件使用的所有类,如下面的演示所示

Enter 开始编辑

如果您不使用这些类,您可以禁用它们来清理 DOM。有关说明,请参阅禁用默认 CSS 类

覆盖子组件插槽

如果您想更改组件渲染的 HTML 结构,可以使用 slots 和/或 component 属性覆盖默认子组件(“插槽”)——有关更多详细信息,请参阅 Base 用法页面上的“共享属性”

以下演示使用 Switch 来展示如何通过将样式应用于其三个子组件插槽来创建样式化组件:rootthumbinput

请注意,虽然此演示使用 MUI System 作为样式解决方案,但您可以自由选择任何替代方案。

Enter 开始编辑

您在 slots 属性中传入的组件从顶级组件(“所有者”)接收 ownerState 属性。按照惯例,它包含传递给所有者的所有属性,并与其渲染状态合并。

例如

<Switch slots={{ thumb: MyCustomThumb }} data-foo="42" />

在这种情况下,MyCustomThumb 组件接收具有以下数据的 ownerState 对象

{
  checked: boolean;
  disabled: boolean;
  focusVisible: boolean;
  readOnly: boolean;
  'data-foo': string;
}

您可以使用此对象来样式化您的组件。

如果您需要使用 ownerState 将某些属性传播到第三方组件,则必须为此目的创建一个自定义包装器。但是,如果您不需要 ownerState 并且只想解决错误,则可以使用 prepareForSlot 实用程序

Enter 开始编辑

自定义插槽 props

使用 slotProps 属性自定义内部组件 props。最常见的用例是设置类名,但您可以设置任何 prop,包括事件处理程序。

以下示例展示了如何向 Switch 的两个插槽添加自定义类

function Switch(props: SwitchProps) {
  const slotProps: SwitchProps['slotProps'] = {
    thumb: {
      className: 'my-thumb',
    },
    track: {
      className: 'my-track',
    },
  };

  return <Switch {...props} slotProps={slotProps} />;
}

switch:thumbswitch:class 是无条件添加的——它们将始终存在于 Switch 组件上。

您可能需要在组件处于特定状态时才应用类。一个很好的例子是根据 Switch 的选中状态向其添加 onoff 类,如下面的演示所示

Enter 开始编辑

在这里,根插槽接收的是回调函数,而不是带有 props 的对象。它的唯一参数是 ownerState,它是一个描述“所有者组件”(在本例中为 Switch)状态的对象。ownerState 保存所有者组件的所有 props(在适用情况下应用默认值),并使用组件的内部状态进行增强。在 Select 组件的情况下,附加信息包括 checkeddisabledfocusVisiblereadOnly 布尔字段。

使用 Hook 创建自定义组件

如果您需要完全控制组件渲染的 HTML 结构,可以使用 Hook 构建它。

Hook 使您可以访问组件使用的逻辑,但没有任何默认结构。有关更多详细信息,请参阅 Base 用法页面上的“组件 vs. Hook”

Hook 返回组件的当前状态(例如 checkeddisabledopen 等),并提供返回 props 的函数,您可以将这些 props 应用于完全自定义的组件。

对于 Switch,该组件附带 useSwitch Hook,它为您提供所有功能,而没有任何结构。

它返回以下对象

{
  checked: Readonly<boolean>;
  disabled: Readonly<boolean>;
  readOnly: Readonly<boolean>;
  focusVisible: Readonly<boolean>;
  getInputProps: (otherProps?: object) => SwitchInputProps;
}

checkeddisabledreadOnlyfocusVisible 字段表示开关的状态。使用它们将样式应用于您的 HTML 元素。

getInputProps 函数可用于获取放置在 HTML <input> 上的 props,以使开关可访问。

Enter 开始编辑

禁用默认 CSS 类

如果您不需要组件上的内置类,可以禁用它们。这将清理 DOM,并且在您应用自己的类或使用 CSS-in-JS 解决方案样式化组件时尤其有用。为此,请将您的组件包装在 ClassNameConfigurator 组件中(从 @mui/base/utils 导入)

<ClassNameConfigurator disableDefaultClasses>
  <Button>I'm classless!</Button>
</ClassNameConfigurator>

检查以下演示中的元素以查看差异

Enter 开始编辑