Web UI 开发乍一看可能很简单,但深入研究后,您会发现许多复杂性,即使是经验丰富的开发人员也会面临挑战。本文旨在剖析 Web UI 开发的固有挑战,从 Web 语言与现代 UI 要求之间的差异,到复杂的数据管理问题和异步 API 调用。
我们还将探索经常被忽视的“不愉快的路径”,例如加载状态、错误处理以及包括安全性、性能和可访问性的更广泛的架构考虑因素。
导航语言不匹配除非您正在构建一个简单的、类似文档的网页(例如没有搜索框或模态等高级 UI 元素的基本文章),否则网络浏览器提供的内置语言通常是不够的。大多数 Web 应用程序比简单的文档复杂得多。
网络语言与人们日常遇到的 UI 体验之间存在巨大差异。无论是票务预订平台、项目管理工具还是图片库,现代 Web UI 都很复杂,并且本地 Web 语言不容易支持它们。您可以加倍努力来“模拟”UI 组件,例如手风琴、切换开关或交互式卡,但从根本上讲,您仍然在使用相当于文档的内容,而不是真正的 UI 组件。
在理想的世界中,构建用户界面就像与视觉 UI 设计师一起工作。 C++ Builder 或 Delphi 等工具,或者 Figma 等更现代的替代品,让您可以将组件拖放到画布上,然后在任何屏幕上无缝渲染。
Web 开发的情况并非如此。例如,要创建自定义搜索输入,您需要将其包装在其他元素中,微调颜色,调整填充和字体,也许还添加一个图标以供用户指导。创建一个出现在搜索框正下方的自动建议列表,并与其宽度完全匹配,通常比人们最初想象的要耗费更多的人力。
上面是一个 Jira 票证——一个问题视图,你可以看出它是一个相对复杂的用户界面。对于这样的 UI,您可能期望有 Navigator 组件、 Dropdown 列表、 Accordion 等等。但不存在,或者我应该直接说不存在。
开发人员努力使用 HTML、CSS 和 JavaScript 来模拟这些。如果我暂时禁用网站的 CSS,我会得到如下结果:
感谢 Jira 工程师的辛勤工作,尽管该应用程序看起来不太正常,但它仍然可以正常运行 - 您可以单击按钮和链接进行导航,甚至上传图像作为工单的附件。
利用设计系统库可以缓解其中一些挑战,为开发人员提供领先优势,而不是从头开始构建。然而,设计系统有其自身的一系列缺点,需要在集成到项目中之前进行彻底的评估。
不幸的是,语言不匹配只是问题的一小部分,坦率地说,自从我们有了像 React 和 Vue 这样的声明式 UI 库以来,情况已经发生了轻微的变化。然而,前端领域还存在其他挑战。数据(状态)管理肯定在名单上。
了解状态管理在现代前端开发中管理状态是一项复杂的任务。几乎每个应用程序都必须通过网络从远程服务器检索数据。这通常涉及各种问题,例如 API 访问的身份验证和数据获取代码的细节,例如使用的协议、数据格式以及 API 是 RESTful、GraphQL 还是基于 RPC。
但这只是一方面;还有管理当地国家的问题。例如,手风琴组件需要跟踪它是展开还是折叠,表单中的文本字段必须管理其自己的值。
当您的应用程序达到使状态跟踪变得困难的复杂程度时,使用 Redux 或 MobX 等第三方状态管理库可能会很有帮助。然而,这种方法并非没有警告,应该仔细考虑。
许多开发人员倾向于使用 React 的内置 Context API 进行状态管理。其他复杂性(例如陡峭的学习曲线、样板代码和潜在的性能开销)是这些库可能不适合所有人的一些原因。
探索不愉快的道路当谈到 UI 开发时,我们的主要关注点通常是“幸福之路”——一切按计划进行的最佳用户旅程。然而,忽视“不愉快的路径”可能会让你的用户界面比你最初想象的要复杂得多。以下是一些可能导致不愉快路径并最终使 UI 开发工作复杂化的场景。
另一个组件中的错误想象一下您正在应用程序中使用第三方组件甚至其他团队的组件。如果该组件抛出错误,则可能会破坏您的 UI 或导致您必须考虑的意外行为。这可能涉及添加条件逻辑或错误边界来妥善处理这些错误,从而使您的 UI 比最初预期的更加复杂。
例如,我们尝试访问的以下代码在传入的 props 中不存在 - 一个常见的 TypeError: Cannot readproperties of undefined (reading ‘doesnt’) 并抛出异常。
const MenuItem = ({item,onItemClick,}: {item: MenuItemType;onItemClick: (item: MenuItemType) => void;}) => {// @ts-ignoreconst information = item.name + item.something.doesnt.exist;return (<li key={item.name}><h3>{item.name}</h3><p>{item.description}</p><button onClick={() => onItemClick(item)}>Add to Cart</button></li>);};
如果我们不将错误隔离到 ErrorBoundary 中,可能会导致整个应用程序崩溃。
下游系统出现故障您的 UI 可能依赖于各种微服务或 API 来获取数据。如果这些下游系统中的任何一个出现故障,您的 UI 都必须对此负责。您需要设计回退、加载指示器或友好的错误消息来指导用户下一步该做什么。有效处理这些场景通常涉及前端和后端逻辑,从而为 UI 开发任务增加了另一层复杂性。
意外的用户行为无论你的用户界面设计得多么完美,用户总会找到以你意想不到的方式使用你的系统的方法。无论他们在文本字段中输入特殊字符、尝试过快提交表单,还是使用干扰您网站的浏览器扩展,您都必须设计 UI 来处理这些边缘情况。这意味着实施额外的验证、检查和保护措施,这可能会使您的 UI 代码库变得复杂。
了解并有效管理这些不愉快的路径对于创建健壮、有弹性且用户友好的界面至关重要。它们不仅使您的应用程序更加可靠,而且还有助于提供更全面、经过深思熟虑的用户体验。
我之前在这篇文章中讨论过不幸的路径,如果你还没有阅读过,请阅读。
通过网络访问远程状态在 React 中,网络编程带来了独特的挑战,超出了使用 useEffect 进行 API 调用的表面简单性。虽然幸福的道路可能看起来很简单,但当您考虑错误处理、加载状态和重试等各种场景时,现实很快就会变得复杂。再加上缓存的复杂性,代码很快就会变得笨拙且难以维护。
因此,不要使用 useEffect 钩子这样做(尽管这是一个很好的起点)。
const [users, setUsers] = useState<User[]>([])useEffect(() => {const fetchUsers = () => {fetch('https://jsonplaceholder.typicode.com/users').then((response) => response.json()).then((data) => setUsers(data));}fetchUsers();}, []);
在现实世界的代码库中,除了使用 fetch 函数之外,您经常会发现自己管理其他状态,这个过程也容易出错。
const [loading, setLoading] = useState<boolean>(false);const [error, setError] = useState<string>('');useEffect(() => {const fetchUsers = () => {setLoading(true);fetch('https://jsonplaceholder.typicode.com/users2').then((response) => {if (response.status >= 200 && response.status <= 299) {return response.json();} else {setLoading(false);throw Error(response.statusText);}}).then((data) => {setLoading(false);setUsers(data)}).catch((e) => {setLoading(false);setError(e);});}fetchUsers();}, []);if(error) {return <ErrorMessage />}
React-query 文档页面上有一长串潜在的注意事项。
其他考虑因素我们已经讨论了 Web UI 开发中的一些关键挑战,但这只是您在实际项目中遇到的复杂性的一小部分。在应对这些和其他挑战时,必须考虑更多因素。
跨浏览器兼容性:不同的浏览器具有不同的渲染引擎,甚至同一浏览器的不同版本也可能表现不同。这使得跨浏览器兼容性成为一个主要问题。
性能优化:确保网站快速高效需要深入了解浏览器如何渲染元素、延迟加载、优化资源等等。
无障碍:建立一个可供所有人(包括残疾人)使用的无障碍网站需要额外的知识和测试。
安全问题:随着前端应用程序的兴起,客户端安全变得更加重要。考虑跨站点脚本 (XSS)、跨站点请求伪造 (CSRF) 和通过客户端日志泄露数据等问题。
虽然上面的每个公告都可以有自己的文章甚至一系列文章,但我将在这里停下来,以免这篇文章变得过于冗长和令人难以承受。我为那些有兴趣深入研究的人提供了一个参考部分,其中包含进一步的阅读材料。
概括总而言之,Web UI 开发领域充满了超出编写代码和设计界面的挑战。固有的语言限制、细致入微的数据管理、异步复杂性以及经常被忽视的不愉快路径共同使这个领域成为一个强大的领域。
围绕安全性、性能和可访问性的架构决策使情况进一步复杂化。了解这些挑战是有效应对这些挑战并打造具有视觉吸引力且强大、安全且用户友好的 Web UI 的第一步。