发布于 2026-01-06 2 阅读
0

用户界面组件的接口

用户界面组件的接口

近年来,前端开发已成为我生活中不可或缺的一部分。但几年前刚入行时,我对API一无所知。虽然我接触过API,但从未真正关心过它究竟是什么,或者构建一个API需要哪些步骤。我了解用户界面(UI)中的接口概念,但它与API中的“I”之间的联系却让我一头雾水。后来,协作变得尤为重要。你的同事应该能够使用并理解你的工作。正是在那时,我开始意识到前端开发中API与UI之间的联系。

什么是接口?

作为前端工程师,务必始终考虑工作的可复用性。另一方面,我们的工作也应该对用户友好且易于访问。这两个概念与现代工作方式相辅相成,而设计系统正是其中的核心。正如Alla Kholmatova 在她的书中描述的那样,设计系统由可复用的模式构成。但是,如何才能使某些东西可复用呢?尤其是在内容模式本身相当复杂的情况下?

这就涉及到接口的概念了。一向可靠的维基百科对接口的定义如下。

接口是计算机系统中两个或多个独立组件交换信息的共享边界。

当我用前端视角审视这个定义时,我首先看到的是“组件”这个词。两个或多个相互协作的独立 UI 组件正是我们构建大多数设计系统的方式。例如,在React中,你可以通过子组件的props将数据从父组件传递给子组件。那么,这是否就是前端开发中设计和开发界面的关键所在呢?没错,正是如此。

正如前文所述,界面发挥作用的地方远不止于此。当用户点击按钮或填写并提交表单时,他/她实际上是在与我们的一个或多个组件进行交互。用户交互的用户界面是界面定义中共享的边界。用户的交互行为是向组件传递其意图信息的一种方式。

组件解剖

因此,在设计和开发组件时,我们需要处理两种类型的界面。通过组合多个这样的组件,我们可以创建一个用户可以使用的、连接到我们系统的用户界面。太好了!这就完成了吗?还没有完全完成。当我们更改一个界面中的某些内容时,它会影响同一组件的其他界面。为了更好地理解这一点,我们需要了解组件的结构。

UI组件结构

如您所见,UI 组件由多个相互交互的部分组成。当用户点击按钮与 UI 交互时,组件内部会触发一些逻辑。根据这些逻辑,组件内部会发生多种情况:更新组件的内部状态、向后端发送请求或向用户返回信息。然而,组件内部缺少一个重要的功能。组件可以通过其 API 向其他组件提供信息。但这只有在其他组件通过提供回调函数(例如onClick按钮组件的回调函数)连接到您的组件时才能实现。

您的组件可以通过 API 向其他组件提供信息,反之亦然。其他组件也可以通过 API 向您的组件提供信息。这是其他工程师使用的接口。当其他组件通过 API 连接时,您的组件会运行一些逻辑。根据这些逻辑,它会更新其内部状态、返回信息或根据信息更新 UI。

在最后一种情况下,我们的组件会在其 API 中描述如何与其他组件连接。它不仅描述了可以接收哪些类型的信息,还描述了何时可以返回信息(例如回调函数onClick)。我们通常可以假设其他工程师并不了解我们 UI 组件的内部机制。因此,接口就成为了一种描述我们希望其他人如何使用我们的工作以及如何与之交互的方式。但是,我们如何描述我们的接口,才能确保其他人知道如何与它们交互呢?

界面成为描述我们希望他人如何使用我们的作品并与之互动的一种方式。

描述接口

对于您的用户而言,这个问题已经通过合理的设计部分得到解决。为用户提供清晰的视觉提示,让他们知道在哪里以及如何与您的组件交互,是良好的第一步。第二步在于如何实现这些设计。并非所有用户都会按照您设想的方式与用户界面交互。这可能由多种原因造成,其中一个重要原因是残障。例如,当用户部分失明时,他们可能会使用屏幕阅读器来与您的组件交互。用户界面的设计本身无需改变,但在实现层面,需要考虑这些用例。这被称为可访问性(或无障碍设计a11y)。

然而,在本文的其余部分,我想讨论一下工程师界面或 API。描述我们希望其他工程师如何与我们的 UI 组件交互并非易事。作为工程师(包括我自己),我们常常觉得自己的工作无需过多解释。但事实并非如此。我们至少需要描述一些内容,以确保不同级别的工程师都能在需要时使用我们的工作成果。

  • 他们可以访问我们 UI 组件的哪些 API?
  • 对于每个 API,说明其使用方法和用途(例如,说明如何影响 UI 组件的样式);
  • 示例展示了实际结果(用户界面)以及不同 API 输入组合的影响。

您可以通过多种方式实现上述目标。您可以编写详尽的 Markdown 文档(.md例如 `.markdown.js` README.md)。一个有趣的选择是构建一个文档网站,您可以在其中与组件进行交互。如果这需要投入过多资金,那么使用GitbookStorybook等工具也是不错的选择,它们可以低成本地为 UI 组件编写详尽的文档。

React API 指南

到目前为止,文字很多,示例太少(抱歉,我的错)。所以,我们来讨论一些使用 React 描述 API 的要点。希望你能发现,这些示例也适用于其他框架。在 React 中,你的 API 就是你定义的props。我们来看一个带有属性的小按钮示例。

const Button = ({ onClick, variant, children, override, className, type }) => {
  return (
    <button
      onClick={onClick}
      type={type}
      className={`${override.defaultClassName} ${className}`}
      data-variant={variant}>
      {children}
    </button>
  );
};

Button.propTypes = {
  variant: PropTypes.oneOf(['primary', 'stroke', 'flat']).isRequired,
  onClick: PropTypes.func.isRequired,
  override: PropTypes.object
};

Button.defaultProps = {
  variant: 'primary',
  className: '',
  override: {
    defaultClassName: 'btn'
  }
};
Enter fullscreen mode Exit fullscreen mode

在顶部,我们看到实际的组件。在return语句中,我们可以看到生成的 UI,也可以看到如何应用props。更重要的是这里的 `type`Button.propTypes和 ` required`。前者是 React 中描述每个propButton.defaultProps的预期值类型以及它们是否必需的一种方式。对于 `required` prop,我们还可以看到我们限制了它可以取的值。variant

我们通过defaultProps定义组件使用的一些默认值来解决这个问题。使用默认值可以有效避免用户在使用组件时产生不必要的副作用。如果不定义默认值className,组件将返回 ` undefinedundefined`。但是,由于我们设置了空字符串作为默认值,因此组件会使用空字符串代替 `undefined` undefined。这样可以避免用户创建名为 `undefined` 的 CSS 类时可能出现的潜在副作用。

其中一个可能看起来有点奇怪的属性是`props`override。假设你为按钮使用了一个名为 `button` 的默认类名.btn。虽然这是一个合理且好的名字,但其他开发者在不同的项目中使用不同的默认类名。你可以在override `props`中覆盖一些常用的默认内部变量。理想情况下,这种做法很少用到,但它确实是一种让你的组件更易于他人使用的简单方法。不过,作为开发者,你肯定不想override.defaultClassName每次都设置 `props`。在这种情况下,你可以创建一个新的组件来包装你的Button组件。这样就避免了其他开发者需要了解你组件的内部逻辑。

const PrimaryButton = (props) => (<Button
 variant="primary"
 override={{ defaultClassName='company-btn' }}
 {...props}
/>);
Enter fullscreen mode Exit fullscreen mode

接下来怎么办?

组件的接口设计并非易事。其他使用你的 UI 组件的开发者可能并不关心其内部机制。即便如此,你仍然需要确保他们了解如何使用组件并与之交互。毕竟,最终他们会影响用户界面(UI)。用户也需要理解如何与我们的组件进行交互。在尝试实现这一点时,可以从小处着手(例如 API 的命名规范)。之后,你可以逐步扩展,找到比本文所述更好的接口设计方法。

本文最初发表于kevtiq.co

文章来源:https://dev.to/vyckes/interface-your-ui-components-5c52