覆盖组件结构
了解如何覆盖 Material UI 组件的默认 DOM 结构。
Material UI 组件旨在适应最广泛的用例,但您有时可能需要更改组件结构在 DOM 中的渲染方式。
要理解如何做到这一点,了解 API 设计随时间演变的历程,并对组件本身有一个准确的心智模型会有所帮助。
背景
在 Material UI v6 之前,无法覆盖库中大多数组件的结构。某些组件具有 *Props
props,允许您将 props 传递给特定插槽,但这种模式并未得到一致的应用。
在 v6 中,这些 props 已被弃用,转而使用 slots
和 slotProps
props,这些 props 可以更精细地控制组件的结构,并使整个库的 API 更加一致。
心智模型
组件的结构由填充该组件插槽的元素决定。插槽最常由 HTML 标签填充,但也可能由 React 组件填充。
所有组件都包含一个根插槽,用于定义它们在 DOM 树中的主要节点;更复杂的组件还包含额外的内部插槽,这些插槽以它们所代表的元素命名。
所有非实用程序 Material UI 组件都接受两个 props 来覆盖其渲染的 HTML 结构
component
—覆盖根插槽slots
—替换任何内部插槽(如果存在)以及根插槽
此外,您可以使用 slotProps
将自定义 props 传递给内部插槽。
根插槽
根插槽表示组件的最外层元素。它由带有适当 HTML 元素的样式化组件填充。
例如,Button 的根插槽是一个 <button>
元素。此组件仅具有一个根插槽;更复杂的组件可能具有额外的内部插槽。
component prop
使用 component
prop 覆盖组件的根插槽。下面的演示展示了如何将 Button 的 <button>
标签替换为 <a>
以创建链接按钮
内部插槽
复杂组件由一个或多个内部插槽以及根插槽组成。这些插槽通常(但并非总是)嵌套在根插槽内。
例如,Autocomplete 由一个根 <div>
组成,该根 <div>
包含多个内部插槽,这些插槽以它们所代表的元素命名:input、startDecorator、endDecorator、clearIndicator、popupIndicator 等。
slots prop
使用 slots
prop 替换组件的内部插槽。下面的示例展示了如何替换 Autocomplete 组件中的 popper 插槽以删除弹出功能
slotProps prop
slotProps
prop 是一个对象,其中包含组件内所有插槽的 props。您可以使用它来定义要传递给组件内部插槽的其他自定义 props。
例如,下面的代码片段展示了如何将自定义 data-testid
添加到 Autocomplete 组件的 popper 插槽
<Autocomplete slotProps={{ popper: { 'data-testid': 'my-popper' } }} />
放置在主组件上的所有其他 props 也将传播到根插槽中(就像它们放置在 slotProps.root
中一样)。以下两个示例是等效的
<Autocomplete id="badge1">
<Autocomplete slotProps={{ root: { id: 'badge1' } }}>
最佳实践
当您需要覆盖元素同时保留插槽的样式时,请使用 component
或 slotProps.{slot}.component
prop。
当您需要使用自定义组件替换插槽的样式和功能时,请使用 slots
prop。
使用 component
进行覆盖可让您将该元素的属性直接应用于根元素。例如,如果您使用 <li>
标签覆盖 Button 的根元素,则可以直接将 <li>
属性 value
添加到组件。如果您对 slots.root
执行相同的操作,则需要将此属性放在 slotProps.root
对象上,以避免 TypeScript 错误。
在覆盖更复杂组件的插槽时,请注意您渲染的 DOM 结构。如果您过度偏离默认结构,很容易破坏语义化和可访问 HTML 的规则——例如,无意中将块级元素嵌套在内联元素内部。