首页  >  文章  >  web前端  >  使无头组件易于设计

使无头组件易于设计

王林
王林原创
2024-08-23 14:30:35391浏览

Making headless components easy to style

无头组件只是一个无样式组件,还是还有更多内容?

网络已经通过要求定义样式将样式与内容分开
在 CSS 中而不是 HTML 中。这种架构允许每个网页都采用全局
设计标准,无需定义任何特定于页面的样式。

随着网络演变成一个应用程序平台,开发人员寻找方法
他们不断增长的代码库更易于维护。如今,事实上的策略
组织应用程序代码就是定义小型、轻量级的组件,这些组件可以
一起组成。因此,组件成为
中的组成单位 现代网络开发。

为了封装,组件通常同时定义 HTML 和 CSS。
虽然这使它们更容易创作,但它们可能更难
紧密结合到现有的设计系统中。这一点尤其正确
适用于从外部供应商导入的第三方组件。

无头组件通过重新引入
之间的分离来解决这一挑战 内容和风格。然而现在分离是沿着组件边界的
反对 HTML 和 CSS 之间的关系。它们是创建出色的无头组件的关键
关键在于设计组件的接口,以便开发人员可以
清晰、轻松地应用自己的风格。

转发相关道具

从最基本的意义上来说,无头组件只是一个无样式的组件。
开发人员必须能够将自己的 CSS 应用到
的 HTML 元素 组件定义。

对于简单的组件,这可能只是转发 className
prop 到根元素,以便开发人员可以在他们的
中使用类选择器 CSS。

如果您的组件与原生 HTML 元素具有相同的语义,您可以使用
React 中的 ComponentProps 类型,以确保所有相关的 props 都是
可转发。请记住省略任何您不希望用户使用的道具
您的组件能够覆盖。

import { type ComponentProps } from 'react'

function SubmitButton({ ...props }: Omit<ComponentProps<'button'>, 'type'>) {
  return <button type="submit" {...props} />
}

提供预定义类

对于包含一个或多个子元素的组件,开发人员可能会
想要单独设置每个元素的样式。

支持这一点的一个策略是依赖
CSS 组合器。
例如,无头画廊组件的样式可能如下:

/* Root container */
.gallery {
}

/* Gallery items container */
.gallery > ul {
}

/* Gallery item */
.gallery > ul > li {
}

/* Next and Previous buttons */
.gallery button {
}

但是这种方法产生了一个巨大的问题,因为现在
的内部HTML结构 该组件是其公共 API 的一部分。这会阻止您修改
稍后构造,而不会破坏下游代码。

更好的策略是为每个主要子元素预定义类。这边
开发人员可以使用类选择器,而不依赖于任何特定的 HTML
结构:

.xyz-gallery {
}

.xyz-gallery-next-button {
}

.xyz-gallery-previous-button {
}

.xyz-gallery-items-container {
}

.xyz-gallery-item {
}

记住给你的类添加前缀,这样它们就不会与
冲突 开发者自己的风格。

支持自定义布局

提供预定义的类可能是使开发人员能够
的最快方法 设置你的组件的样式。然而,这种方法的一个缺点是
HTML 结构无法自定义。

这可能并不重要。毕竟,纯 HTML 的使用方式已经相当灵活了
可以渲染。然而,有时开发人员会按顺序获取额外的 HTML
来完成某些设计。如果您查看几乎所有
的源代码 网站,您可能会看到大量无语义的

。元素,
其唯一目的是定义弹性或网格布局,直观地对子元素进行分组
边框内的元素或创建新的堆叠上下文。

您可以通过将无头组件拆分为
来支持此类用例 多个相关组件。这样开发者就可以自由添加自己的
将元素布局到组件。例如,开发人员可以嵌入 Next 和
自定义 Flexbox 容器中图库示例中的上一个按钮:

<Gallery>
  <GalleryItems className='gallery-items-container'>
    {data.map((item) => (
      <GalleryItem key={item.id}>{item.content}</GalleryItem>
    ))}
  </GalleryItems>
  <div className='gallery-buttons-container'>
    <GalleryPreviousButton>
    <GalleryNextButton>
  </div>
</Gallery>
.gallery-items-container {
}

.gallery-buttons-container {
  display: flex;
  gap: 0.5rem;
  justify-content: flex-end;
}

这些类型的组件通常使用
来实现 要传递的上下文
它们之间的数据。他们需要更多的工作来设计、实施和
文档。然而,它们所带来的多功能性通常意味着额外的努力
值得。

允许组件被覆盖

少数用例需要无头组件来管理布局
其子组件。一个例子可能是分层树视图
允许通过拖放对其项目进行重新排序。另一个用例可能是
允许单页应用程序用
替换默认的锚元素 有助于客户端路由的自定义链接组件。

An advanced strategy for allowing developers to define custom layouts is to
allow the actual child component being rendered to be overriden via props:

<TreeView
  nodes={[...]}
  components={{
    CustomRow,
    CustomDragPreview: (props) => <div className="drag-preview" {...props} />
  }}
/>

This grants the developer full control over what is rendered in each child
component, while allowing the headless component to manage its overall
structure.

You can even allow developers to customise the root element of your component
via a prop. For example, this button component allows a developer to render it
as something else:

import { type ElementType } from 'react'

function HeadlessButton({ as, ...props }: { as?: ElementType }) {
  const Component = as ?? 'button'
  return <Component {...props} />
}

For example, in order for assistive technology to treat the button like a link,
the developer can specify that an anchor element should be used to render the
button:

<HeadlessButton as="a">Actually a link</HeadlessButton>

Summary

Headless components are much more than components that don't contain any
styles. Great headless components are fully extensible and allow the developer
to customise the entire internal HTML structure.

以上是使无头组件易于设计的详细内容。更多信息请关注PHP中文网其他相关文章!

声明:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn