我们将介绍更高级别的问题,例如清晰的编码标准,设计模式等。
多年来,我一直在研究一些单页Web应用程序的UI,并希望分享我的一些经验。
在本系列文章中,我将内容分为三个部分:“UI架构级思想”,“大型UI设计”和“UI测试与自动化”。
![025af495af48b80577170009c80997b7.png](https://i-blog.csdnimg.cn/blog_migrate/da0f74fb1a12d3b3dbc63a90b291070b.jpeg)
1. UI架构级思想
无论何时创建新的Web应用程序,始终首先实现本地化。否则,当您要将此功能添加到开发的Web应用程序时,将会有巨大的重构成本和回归测试成本。如果您不想在开始时花费很多时间来实现本地化系统,则可以先定义通用本地化API或未实现的接口,然后再实现完整功能。
本地化包含以下几个方面:
- 语言翻译。
- 时区转换。
- 日期,时间,货币,数字格式和解析。
如何使代码清洁和可扩展,低维护
这是计算机科学中的常见问题。在这里,我想将我的范围限制在前端。答案如下:
始终尽力将UI元素构建为可广泛重用且可配置的组件。
虽然在HTML和CSS中,实现具有固定大小,固定位置和其他固定配置的UI元素更容易,但最好将UI元素包装在组件中并使其大小,位置和其他配置动态依赖于尽可能定制配置或其父级的大小和位置。当您的Web应用程序变得越来越大时,您的PM将始终要求您在其他位置和上下文中重用某些组件。例如,假设有一天您在静态页面中实现了侧边栏。发布后,这个侧边栏成为用户中的热门组件。然后您的PM可能会要求您将侧边栏添加到某些对话框或某些嵌套元素。如果您在实施过程中将所有尺寸,位置和其他配置设置为静态,
使组件可以初始化为尽可能多的状态
虽然一个组件通常有许多配置,但大多数组件都以与插入DOM时相同的状态开始。例如,表组件的初始状态只能在第一页中。树表的初始状态只能是所有根节点都已折叠的树。通常,在从一个状态到另一个状态的转换过程中会有一些额外的逻辑。如果开发人员想要将状态初始化为其他状态(如列表/表中的第2页)或在树表中展开的某个节点,则必须添加额外的逻辑来禁用状态转换逻辑,这会使代码不必要地复杂化。因此,最好将组件初始化为尽可能多的状态,以避免这些状态转换逻辑失败。
在JavaScript中使用面向对象的设计
尽管现代JavaScript版本(如ES6和Typescript)添加了更好的OOD功能,而且许多框架也具有OOD功能,但我之前使用JavaScript的经验主要是非OOD。OOD有一些缺点。它很复杂,需要更多的设计思维,这将导致更长的开发周期。如果您的Web应用程序非常小并且不会变大或者您的Web应用程序主要在后端呈现,那么您不需要使用OOD和JavaScript。您可以使用简单的JavaScript并具有快速迭代和短发布周期。
但是,如果您的应用程序将变得非常大并且JavaScript代码负责所有UI逻辑和呈现(例如单页面应用程序),那么没有OOD的简单JavaScript将使您的代码变得混乱和脆弱。
例如,表UI库可以在不同的组件中使用,而UX部门或您的PM希望在整个项目中具有一定程度的一致性。如果没有OOD,开发人员只需创建一个公共函数来初始化表库的配置,该库可能包含属性设置和回调绑定。由于每个组件都有自己的要求,开发人员只需将所有需求放在公共函数中,并使用id属性来区分不同的组件; 这样做可能会导致此功能变得一团糟。另一个问题是当一个开发人员想要在这个函数中添加新功能时,很容易打破其他功能或一些特殊和隐藏的场景,
想象一下,如果我们与OOD这样做。代码将变得更清晰,更易于扩展,并且更易于维护和测试。我们可以按层次顺序对不同的UI组件建模,在根类中具有基本功能以初始化所有组件的公共配置,并且在每个组件级别实现重写的功能以添加特殊配置。如果我们想要为组件子集添加新功能,我们也可以使用OOD模式实现此功能,例如interface或mixins。我们可以为每个类实现一个简单的单元测试。通过执行上述所有操作,新功能或新配置通常只会影响一小组组件,并且这些级别单元测试可以保护稳定性,开发人员可以通过查看类层次结构轻松找到其修改会影响哪些组件。
因此,我认为使用JavaScript的OOD用于大型和重量级的JavaScript应用程序将使代码清洁和可扩展,并降低大量维护成本。
使您的代码遵循您的业务逻辑基础架构并尽可能独立于数据结构
即使使用OOD,仍然有空间来提高可重用性并减少冗余。例如,假设您要扩展一个类并覆盖超类的函数。您有两种选择:
- 通过复制代码并对复制的代码进行修改来覆盖该功能。
- 首先调用重写的函数,然后添加额外的代码。
后一个选项显然更好,但问题是并非所有逻辑都可以添加到旧逻辑的结尾或开头。某些逻辑可能需要插入旧逻辑的中间。某些逻辑可能需要在旧逻辑中交换两行。有时为了改变函数输入参数的数据结构,开发人员必须彻底改变旧代码的结构。例如,旧输入参数可以是数组,而新输入可以是地图。所以在上面的例子中,我们必须使用第一个选项。但是,它使代码变得多余。冗余代码不仅看起来很糟糕,而且还会让开发人员忘记将代码更改在一个地方同步到多个冗余位置。因此,即使使用第一个选项,我们也应该尽可能地减少冗余。
为了解决这个问题,我认为开发人员应该通过明确定义业务逻辑并从这个逻辑到代码的良好映射来遵循他们自己的业务逻辑基础架构。
所有代码都用于实现业务逻辑。我认为糟糕的可重用性总是由代码函数到业务逻辑的错误映射引起的。有时我们将一堆业务逻辑相关代码混合成一个大函数。稍后,为了实现新功能,可能需要相同业务逻辑的不同数据结构,或者可能只需要业务逻辑的前三分之一,在这种情况下,我们必须重构旧代码或复制旧代码码。有时,当我们定义函数时,我们只考虑了直接需求,并试图找到更简洁的实现,同时忽略了我们未来的需求。之后,也可能需要类似的业务逻辑,但没有简单的方法来重用以前的高度优化的代码。有时为了在语言或图书馆中使用一些技巧,我们将部分业务逻辑放在一个函数中,将其他逻辑放在另一个函数中。之后,可能需要以不同的方式实现整个业务逻辑。
为避免上述错误,我们应该经历两个步骤。
- 我们应该明确定义和组件化业务逻辑。
- 我们应该创建一个非循环图来表示所有业务逻辑。每个顶点都是一个表示业务逻辑的组件,可以用几个词来描述。每个边缘都是一种关系。例如,业务逻辑A可以由业务逻辑B,C和D组成,A可以在E和F中使用。我们应该尽力将一个业务逻辑划分为尽可能多的业务逻辑子集。增加其重用的潜力。
- 我们应该在代码中为每个业务逻辑定义一个函数。
- 我们应该通过使用对象作为输入而不是任何特定的数据结构来使函数尽可能独立于数据结构,因为将来我们可能希望使用不同的数据结构或输入来实现相同的逻辑。如果确实需要特定的数据结构作为输入,我们可以创建一个包装函数,其输入是一个数据结构,并将调用其中的业务逻辑函数。
在整个项目中使用相同的设计模式
通常,诸如Anguler或Ember之类的框架提供了许多功能,开发人员可以通过不同的方式实现相同的目标。但是,当开发人员想要将不同开发人员以不同模式开发的多个组件集成到一个新的大组件中并在它们之间创建高依赖性时,它会增加额外的复杂性和不稳定性。
在项目的基础设施设计阶段,最好为一些常见问题和问题设置通用设计模式和解决方案。
所有函数都应使用单个对象作为输入参数
与通过名称和参数查找函数的Java不同,JavaScript仅通过函数名称查找函数。调用函数时,开发人员可以输入任意数量的输入参数,其余的将设置为undefined。此功能使开发人员可以轻松更改函数的签名,但这也会使代码变得混乱。
用户操作处理程序只能由用户操作触发
这种说法似乎是多余的。但有时用户操作处理程序在函数调用中重用,或者用户操作可以由函数调用触发。我认为这样做并不好,因为用户操作处理程序可能只包含某个特定用户操作的逻辑,或者它可能是仅为该操作添加的新逻辑。如果从函数调用处理程序,则应禁用此逻辑,这似乎增加了不必要的复杂性。因此,当开发人员编写动作处理程序时,他们应该将不同的部分分开并包装在不同的函数中以便重用。而其他开发人员不应该直接重用处理程序。相反,它们应该在处理程序中重用它们所需的确切函数,或者它们应该重构处理程序代码并将它们需要的内容包装在单独的函数中,然后重用它。
使用Javascript框架集成JavaScript UI库
许多Javascript UI库都是用纯JavaScript编写的,直接使用DOM API或jQuery来创建HTML组件。但是,大多数JavaScript框架都使用模板语言来生成HTML组件。设计模式中也存在差异。例如,一些库使用观察者,而其他库使用事件处理程序。当您希望将JavaScript UI库与JavaScript框架一起使用时,这会带来问题和挑战,因为错误的集成可能会使您的代码更容易出错,维护更少,可扩展性更低。
解决方案是在JavaScript UI库上使用JavaScript框架创建一个包装类,它将所有JavaScript UI库的模式转换为JavaScript框架的模式。应用程序中的所有其他代码只应调用包装类的API,不应直接访问Javascript UI lib的API。您可以将所有Javascript UI lib生成的HTML放在ShadowRoot中,以使标记结构,样式和行为保持隐藏,并与页面上的其他代码分开,以便Javascript UI lib代码不会与您自己的代码冲突。
要将JavaScript UI库生成的HTML与模板语言集成,有两种选择:
- 一些JavaScript UI库可以设置现有HTML代码的样式,使其看起来像预期的那样。它们不会创建新的HTML节点。使用此类功能,您可以先将模板语言渲染为HTML,然后再设置HTML样式。
- 或者您可以先在模板中为JavaScript UI库创建占位符,然后将UI组件呈现到占位符中。