---
title: 前端用户界面问题备忘单
description: 在前端/网络开发人员面试时，改善你必须建立的用户界面的顶级技巧 - 代码结构、管理状态、可访问性等。
---

以下是一些提示，可帮助您在前端/ Web 开发人员面试期间改善您必须构建/设计的用户界面。 这些提示可应用于[用户界面编码面试](/front-end-interview-guidebook/user-interface)和[前端系统设计面试](/system-design/types-of-questions)。

## 概览

- **分解问题**:将问题分解为可以逐步构建的阶段/里程碑，并逐步编写您的代码。
- **经常测试**:完成每个功能后在浏览器中测试 UI，以便您可以尽早捕获漏洞。 较早捕获的错误更容易修复。 确保当前功能正常工作，然后再进行下一个功能。
- **使用 JavaScript 框架（如果可能）**: 如果您选择使用纯 JavaScript 构建复杂的 UI，您的生活将非常艰难，因为代码可能会很快变得非常长而杂乱。 我们建议使用框架构建应用程序和游戏。
- **未雨绸缪，计划周全**:考虑一下您的面试官可能要求您添加下一个功能。 设计代码的方式，使新功能可以轻松添加。

## 组件组织

您如何构建代码？

- **采用容器/表示模式**:为了实现良好的解耦合，渲染代码应该与数据源无关。 将组件分成提供数据的外部组件和根据数据呈现视图的内部无状态组件。 这使视图可以从本地组件/应用状态切换到从网络加载的数据等情况，反之亦然，只要更改外部组件，内部组件就可以按原样使用。
- **将应用程序分解为子组件**: 如果 UI 具有多个部分，请将 UI 分解为较小的组件，并确定每个组件需要的属性/状态。
- **最小 API 面积**: 不要将不需要的额外数据传递给内部组件。
- **组件实例化**: 在要求构建 UI 组件时，定义 API（通常是函数）以允许创建多个独立的组件实例，其中包括可配置的选项和默认值。 避免编写代码（例如依赖全局变量）以防止创建单独的 UI 组件实例。
  - **纯 JavaScript**:创建一个接受 DOM 元素（容器元素）和选项对象的函数。 在函数内部，您可以动态创建 DOM 元素并将其附加到容器元素。 组件 API 的另一个灵感来源是[jQuery UI](https://jqueryui.com)，但它依赖于 jQuery。
  - **其他 JavaScript UI 框架**：大多数现代 JavaScript UI 框架（如 React）默认强制您按组件进行思考。

## 状态设计

状态是您的 UI 中随时间而变化的数据，通常是由用户交互或后台事件（网络请求响应、时间流逝、WebSocket 事件）导致的。

面试中的大多数 UI 问题都需要状态，因此设计状态非常重要。

- **确定 UI 中所需的最小状态**: 状态越小，代码的可读性和理解能力就越强 -> 减少错误的可能性。
  - 识别必要状态和派生状态。 派生状态是可以从必要状态计算出的状态。
- **将呈现代码与数据管理代码分开**: 将 UI 作为数据的功能并将代码分为两组（呈现代码和数据管理代码），以获得更好的可读性。 如果使用诸如 React 之类的 JavaScript 框架，则一般强制执行此操作。
- **对于复杂的状态交互，请使用状态-减少器模式**: 如果问题需要许多状态字段，并且某些操作需要同时更改多个字段，请使用[减少器来汇总状态更新逻辑](https://beta.reactjs.org/learn/extracting-state-logic-into-a-reducer)。 由 Redux 首先推广，状态-减少器模式鼓励您确定 UI 的状态、可执行的操作以及如何将操作与状态结合以导出下一个状态。 如果您使用 React，则可以通过[`useReducer` React hook](https://reactjs.org/docs/hooks-reference.html#usereducer)实现此模式。 Redux 通常过于适用于面试，而`useReducer`应该足够。

React 的文档["管理状态"](https://beta.reactjs.org/learn/managing-state)是有关如何正确设计和使用组件状态的杰出资源。 提到的一些想法与 React 无关，可以适用于任何 UI 框架。

## JavaScript

您的 JavaScript 是否使用现代语言语法和优秀实践，同时避免不良实践？

- **使用样式指南**:使用 JavaScript 样式指南，如[Airbnb 的 JavaScript 样式指南](https://github.com/airbnb/javascript)。 在开发过程中，静态分析工具如[ESLint](https://eslint.org)可以帮助您执行其中一些良好实践。 但是，在面试期间可能没有这些工具可用。 尝试适应使用良好编码风格编写代码，而不需要工具的帮助。
- **不要触碰全局环境**:这适用于 Vanilla JavaScript 场景。 避免声明全局变量和全局函数以污染全局范围。 编写立即调用的函数表达式（IIFE）并将所有自定义代码放在其中。

## HTML

您是否使用正确的属性编写语义化的 HTML？

- **语义标签**: 为标题使用标题标签，为交互元素使用按钮标签，为连续元素使用列表标签等。 不要为所有内容使用`<div>`！
- **标题层次**: 确保标题标签具有层次结构，并且 DOM 中没有多个`<h1>`。
- **交互元素**: 使用`<button>`将要求交互的元素包含其中。 不要将单击处理程序添加到`<div>`和`<span>`中。

### 表单

表单本身很复杂，值得有自己的部分。

- **链接标签和输入**: 使用`id`和`for`链接`<label>`中的`<input>`以防止用户意外点击文本。
- **在表单中包装输入**: 将`<input>`包装在`<form>`中，以便单击按钮和按 Enter 将提交表单。 如果使用 Ajax 进行网络请求，则记得添加`event.preventDefault()`。
- **输入应具有适当的类型**: `<input>`应具有适当的类型，例如电子邮件、密码、数字等。
- **利用本机 HTML 表单验证**: 在可能的情况下，结合使用`required`属性和其他属性，如`pattern`、`min`、`max`等，以使用本机 HTML 表单验证。

## CSS/样式

您的 CSS 是以可扩展且易于理解的方式编写的吗？

- **编写 Vanilla CSS**: 学会在没有 processor 的情况下编写 CSS，如[Sass](https://sass-lang.com/)和[Less](https://lesscss.org/)。 并不是所有环境都允许使用处理器，而且面试问题可能很小，不会真正受益于 CSS 预处理器提供的功能。 CSS 处理器最有用的功能是使用变量，这可以通过 CSS 自定义属性（变量）本机提供。
- **采用 CSS 命名约定**: 在编写类时考虑采用 CSS 命名方法论，例如[块元素修饰符](https://getbem.com)。
- **在组件中避免使用`#id`选择器**: 当构建可重用的 UI 组件（例如按钮、选项卡、菜单、模态框等）时，请避免在 HTML 中使用`#id`选择器，因为`id`是全局唯一的，但是您可以多个组件实例。
- **组织您的 CSS**: 阅读有关[在大型项目中组织 CSS](https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/Organizing)和如何具有[可扩展和模块化的 CSS 架构](http://smacss.com/)的文章。

## 用户体验

您的 UI 提供了出色的用户体验吗？

- **适用于移动设备**: 检查您是否需要使 UI 在移动设备上正常工作。
  - CSS 媒体查询可用于在移动设备上显示不同的布局。
  - 使交互元素（如按钮）足够大，以便按下（建议至少为 44 x 44 px），并且间距足够宽。
- **错误状态**: 及时清晰地反映错误-表单验证错误、网络请求错误等。
- **处理不同尺寸的图片**: 使您的 UI 适用于呈现所有大小/维度的图片并保留原始纵横比的情况。
  - 使用 CSS`background-image`和`background-position: contain`，以使图片适合在您定义的区域内。 如果可以将图片剪切（例如渐变背景），请使用`background-position: cover`。
  - `<img>`标记具有类似的`object-fit`属性和`contain`和`cover`值。
- **乐观更新**: 高级技术，即使在网络请求仍在进行时也能反映出成功状态。 如果请求失败，请恢复 UI 更改并显示错误消息。

## 网络

您的 UI 处理网络请求和条件的不可预测性情况吗？

- **反映网络请求状态**: 如果 UI 涉及进行网络请求，请清楚地显示请求的挂起/成功/失败状态。
  - **正在挂起**: 禁用字段/按钮，显示旋转器。
  - **错误**: 显示错误消息。
  - **成功**: 更新 UI 和/或显示成功消息。
- **竞争条件**: 常见的原因是由于并行网络请求，响应顺序不能保证。 稍后发出的请求可能会更早地收到响应。 如果您的 UI 容易受到此类问题的影响，则可以跟踪最新的请求并忽略早期结果。 或者，使您的 UI 不能同时触发多个网络请求，例如通过在单击后禁用触发网络请求的元素等。
- **避免重复请求**: 提交后应禁用按钮以避免进行重复的网络请求。
- **合并请求**: 如果 UI 正在进行太多的网络请求，则可以：
  - 节流/去抖动: 限制发出的网络请求数。
  - 批量请求: 将请求组合在一起并仅进行一次请求。 这需要服务器端支持这种格式。
- **缓存**: 如果最近已经使用相同的参数进行了请求，则您可以重用之前的响应并省略网络往返。
- **请求超时**: 如果请求在规定的持续时间内没有接收到响应，您可能希望人为地显示请求失败（超时）。
- **乐观更新**: 高级技术，即使在网络请求仍在进行时也能反映出成功状态。 如果请求失败，请恢复 UI 更改并显示错误消息。

## 可访问性（a11y）

在 UI 中处理可访问性是一个很大的优势，在某些情况下，老年工程师们需要处理可访问性。

- 您能否仅使用键盘使用 UI？
- 您是否可以使用屏幕阅读器使用您的 UI 组件？
- 您的 UI 组件可以在没有颜色的情况下工作吗？
- 您的 UI 组件可以在没有声音的情况下工作吗？

来源：[网页的可访问 UI 组件](https://medium.com/@addyosmani/accessible-ui-components-for-the-web-39e727101a67)

- **屏幕阅读器、ARIA 角色、状态和属性**:
  - 为不使用自定义 HTML 标记构建的元素添加正确的`aria-role`。
  - 使用`aria-label`来描述其中没有文本显示的元素（例如仅图标的按钮）。
  - 通过`aria-describedby`/`aria-errormessage`将错误消息元素链接到负责它们的元素。
  - **图片“alt”文本**:为`<img>`元素添加`alt`属性，以便屏幕阅读器可以描述图片。
- **键盘交互**
  - 将`tabindex`属性添加到要通过键盘制表符聚焦的元素。
  - 元素可以通过键盘触发。
  - 检查焦点顺序是否合理。
- **视觉问题**
  - **颜色对比度**: 文本/图片与背景之间的足够颜色对比度。
  - **元素大小**: 字体大小，交互元素大小应足够大，适合其预期的媒介。

Google 的[web.dev]有一个[免费的深入课程](https://web.dev/learn/accessibility/)，其中详细介绍了可访问性的内容，我们强烈推荐。

## 边缘情况

在面试期间，可能没有足够的时间处理代码中的所有边缘情况，但是最好向面试官提及它们，以获得额外的答案。

- **处理长字符串**: UI 中的字符串（例如标题/按钮标签）可能会导致 UI 表现出奇怪的行为，例如溢出并影响周围元素的位置。 长字符串可能是用户输入或翻译的结果。
- **空状态**: 显示一个空状态消息/占位符以指示内容的缺失，例如列表为空时。 什么也不显示可能会使用户认为仍在等待网络请求并且正在获取数据。
- **列表中的太多项**: 在单个页面上显示太多项目可能会导致糟糕的 UX（用户必须滚动很多）和反应灵敏度和内存消耗的糟糕性能。
  - **分页**: 将长列表分成多个页面。
  - [**虚拟列表**](https://www.patterns.dev/posts/virtual-lists):在动态列表中仅呈现可见行的内容，而不是整个列表。
  - 截断多余的内容，并显示一个省略号。 `word-break` CSS 属性非常有用。
  - 将内容限制在前 X 个字符/单词，并在“显示更多”按钮后面隐藏多余的内容。

## 性能

- **节流/去抖动**: 节流和去抖动是限制频率的技术，可防止不必要的操作。 此技术可用于不是非常时间敏感的操作，例如网络请求和滚动/调整事件回调。
- **缓存**: 可以将计算/网络请求的结果缓存在浏览器内存/存储器中，以避免重复操作。
- **按需加载**: 仅在需要时延迟加载数据/组件代码，而不是一次性加载全部。
- **预取/预加载数据**: 在需要数据之前立即预取/预加载数据，以使更新立即显示，从而减少网络延迟。
- **列表中的太多项**: 参见上面的“边缘情况”下的内容。

## 安全

- **跨站点脚本（XSS）**: 在渲染内容时，请避免分配给[`Element.innerHTML`](https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML)或 React 的`dangerouslySetInnerHTML`，因为它来自用户会导致跨站点脚本，应将其分配给[`Node.textContent`](https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent) 或使用实验性的[`Element.setHTML()`](https://developer.mozilla.org/en-US/docs/Web/API/Element/setHTML) 方法代替。 请参考[OWASP 的 XSS 预防备忘单](https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html)
- **“URL 上下文”的输出编码**: 如果用户提供的输入可能用于 URL 查询参数，请使用`encodeURIComponent`来防止意外的值成为 URL 的一部分（例如额外的查询参数）。
- **跨站点请求伪造**: 请参见[OWASP 的 XSS 预防备忘单](https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html)。

## 国际化（i18n）

您的 UI 能够处理多种语言吗？ 添加支持更多语言有多容易？

- **避免硬编码特定语言的标签**: 一些 UI 组件中具有标签字符串（例如，图片轮播具有上一个/下一个按钮的标签）。 允许使用这些标签字符串进行自定义，使它们成为组件的 props/options 的一部分，这很好。
- **UI 可以呈现长字符串**: 参见上面有关呈现长字符串的部分。
- **从右到左的语言**: 某些语言（例如，阿拉伯语、希伯来语） 使用[CSS 逻辑属性](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Logical_Properties)来使你的布局适用于不同的[书写模式](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Writing_Modes)。
