领域驱动设计和函数式纯 UI 组件
形态学
形态学测试
概括
由 Mux 主办的 DEV 全球展示挑战赛:展示你的项目!
随着用户交互方式和体验数量的激增,应用程序的开发变得越来越具有挑战性。用户现在希望获得丰富、自然、快捷的交互体验,并且需要可靠的应用程序。
现在,随着我们在构建这些复杂应用程序时的需求不断发展,我们拥有了React 、 Vue 、 Svelte 或 Angular 等尖端库和框架 ,仅举几例。
此外,应用程序状态管理本身就是一个挑战,社区为此开发了各种解决方案,例如 Redux 和 MobX 。当涉及到与后端进行 HTTP 请求的异步状态时,这个问题会变得更加复杂。
我个人对分布式架构和模式很感兴趣,但我发现系统前端编程的复杂性也很有意思,因为它本质上就是为了满足用户需求。当我们处理后端每分钟大量的请求,每分钟交换数千兆字节的信息时,很容易忽略用户,而只关注系统本身。
然而,由于前端应用程序的特性,你有机会专注于单个用户,因此你会尝试了解他们的多种需求。尽管我们有很大的学习机会,但遗憾的是,这种情况并不常见,我们往往基于一些基本的 UI 模式来构建应用程序,这些模式对用户和我们开发者来说效率都很低。
如今构建用户界面比以往更加便捷、经济且自动化。然而,大多数用户界面对用户而言成本仍然很高(看看你为一个网页下载的 JavaScript 代码量就知道了),对开发者来说也是如此,因为网页一旦构建完成,更改其结构就非常困难。
我一直在研究如何降低用户界面的修改成本、使其更易于组合和测试。我得出了以下结论,这些结论将有助于简化用户界面的修改:
应用程序需要像黏土一样具有可塑性。
转场效果需要合理且易于追踪。尽量使用一对一转场,尽可能避免使用扇形展开。
默认情况下是异步的,同步代码只是速度非常快的异步代码。
自动测试应用程序应该像在浏览器中渲染它一样简单。
因此,基于敏捷开发和极限编程,我想到了以下用于降低用户界面成本的库或框架的要求。
要使应用程序具有可塑性,就需要经常改变其结构。
让过渡自然流畅,过渡应该是应用程序运行方式的基本组成部分。
库应该以相同的方式理解异步和同步业务逻辑。
应用程序的每个组件都应该能够独立测试且测试速度快。
我编写了一个名为 `<library_name>` 的库 morphonent来实现这些理念。然而,我认为这些模式和设计决策如果真的有用,可以构建在其他更强大、更可靠的库之上,例如上面提到的那些库。这里重要的不是我为了实现这些模式而构建的库,而是这些模式本身。
用于构建 Web 用户界面的 JavaScript 库
形态学
Morphonent 是一个用于构建 Web 用户界面的 JavaScript 库。
功能 齐全,无副作用,结构简单,您的组件将保持合理性。
默认情况下不使用特殊语法 。使用普通函数,无需特殊语法。
体积小 ,无运行时依赖。
默认异步加载 。旨在异步加载组件。
有关 Morphonent 任务的更多信息,请查看这篇 dev.to 帖子。
安装
morphonent 是一个简单的 npm 包,您可以使用 yarn 安装:
$> yarn add morphonent
或 npm:
$> npm install --save morphonent
morphonent 被打包成一个 ES 模块,可以从现代浏览器或用 babel 编译的应用程序中导入。
入门
使用 webpack,您可以在几分钟内搭建一个简单的 Morphonent 应用。您可以 在我们 wiki 上的入门指南中 找到具体步骤。
它长什么样?
如果您想查看一个简单的待办事项清单应用程序示例,请点击这里 。
…
你好世界
如果我们可以看一下代码,可能会更容易理解。我们来看一个 Hello World例子。
如您所见,我们的组件只是一个函数,就像 React 的 函数组件一样。该库默认不支持 JSX,但 README 文件中有关于如何启用 JSX 的教程。为了便于展示,我将使用纯 JavaScript 函数。
互动
组件交互是通过事件实现的,事件由事件处理程序处理。不同之处在于,函数不包含可变状态,就像……一样 React hooks。那么,组件如何改变呢?
事件处理程序需要定义下一个要渲染的组件。这使我们能够专注于行为,而不是状态。例如,让我们来映射一下切换按钮的交互过程:
实现这种模式的代码实际上与图片类似:
如您所见,我们并没有改变按钮的状态,这与我们在可变组件中使用钩子或属性的做法类似。在事件处理程序中,我们返回的是将要处理该事件的函数,而该函数又会返回一个 new component用于处理后续交互的组件。DOM 差异比较和优化将由库本身处理。
这使我们能够定义交互和组件,而无需关心它们的具体实现。组件的挂载不再是一个必须执行的决定。
例如,当我们点击按钮 10 次时,我们可以完全改变 DOM,将按钮变成 span 元素。
现在重要的是交互,而不是组件。组件只是实现细节,决定了事物的渲染方式,而交互不再局限于组件的结构。这使得应用程序更具可塑性。我们可以处理更复杂的场景,例如待办事项列表以及删除条目的功能。
当然,这里的逻辑很简单,但这种思维方式和模式能让我们以更强大的方式构建用户界面。因为之后,我们可以轻松地将交互与不同类型的用户角色绑定,并基于这些角色渲染完全不同的应用程序。
异步组件和转换
通常,交互需要从外部服务收集用户信息,而这些服务可能速度较慢或容易出错。为了解决这个问题,我们的应用程序需要理解交互可能需要缓慢的过渡。为此,我们需要一个更高级别的组件 transition:
交互过程中会发生转换,转换需要两个不同的参数:
我们可以通过以下应用程序来了解它是如何通过查询 GitHub API 获取用户仓库的:
用户画像和动态布局
现在我们可以进一步迭代用户界面,并在仓库列表足够大(15 个仓库)时彻底改变列表布局。如果仓库少于 15 个,我们将只显示一个有序列表 ol。如果超过 15 个,我们将 div使用 flex-box 布局显示多个仓库。这样,贡献较大的用户和贡献较小的用户看到的仓库列表将完全不同。
您可以利用这些信息进行测试:
小额贡献者:kmruiz
主要贡献者:vlingo
您可以使用按钮来查看示例。
你会发现,根据用户信息完全改变布局非常容易,因为这正是整个框架的工作原理。其背后的理念正是如此:组件只是实现细节,重要的是用户交互的方式。
测试
现在到了最后一步:可测试性。我们如何才能让交互和组件易于测试呢?我们可以利用以下几个特性来简化代码的测试:
副作用由用户交互处理。
我们的组件都是纯函数。
交互绑定是在最抽象的层面上完成的。
我个人认为 Enzyme 和 React Test 的工作方式其实不错。主要问题是它们速度相对较慢,因为它们需要处理大量的差异比较和状态逻辑。我为 Morphonent 开发了一个示例测试库,它实现了类似的 Fluent API,但专门针对 Morphonent。使用这个库进行测试,对于小型组件和交互,每个测试通常耗时不到 1 毫秒。
形态学测试
morphonent-test 是一个用于验证用 morphonent 编写的组件的测试库。
简单易用 ,并附带合理的默认设置。
速度快 。完全无需在 DOM 中挂载组件即可运行,便于快速轻松地进行检查。
默认异步 。旨在让异步组件的使用与同步组件的使用一样便捷。
安装
morphonent-test 是一个简单的 npm 包,您可以使用 yarn 安装:
$> yarn add -D morphonent-test
或 npm:
$> npm install --save-dev morphonent-test
入门
morphonent-test 提供了一个组件封装器,它完全用于通过流畅的 API 进行内省并与真实组件交互。我们还将该库设计得对 TDD 友好,因此可以轻松地根据您的需求进行更改和调整。
让我们来看一个使用 jest 作为测试运行器的示例测试。
import { testing , click } from 'morphonent-test' ;
describe ( 'Counter component' , ( ) => {
describe ( 'counting upwards' , ( ) …
Enter fullscreen mode
Exit fullscreen mode
由于测试是在 Node 上运行的,这次我无法分享 Codepen,但我会分享一些代码示例。
如何测试交互
// fake data
const firstId = faker . internet . userName ()
const secondId = faker . internet . userName ()
const text = faker . internet . userName ()
// example components (clicking on firstComponent will render secondComponent)
const secondComponent = () => element ( ' div ' , { id : secondId }, text )
const firstComponent = () => element ( ' button ' , { id : firstId , onclick : secondComponent })
// interactions
const result = await testing ( firstComponent ) // wrap into a test container
. findById ( firstId ) // find the button
. trigger ( click ()) // click it (will return the result of the handler)
. findById ( secondId ) // find the div with the new content
. textContent () // get the text content
// expectations
expect ( result ). toBe ( text )
Enter fullscreen mode
Exit fullscreen mode
概括
我相信这些模式使我们能够专注于用户交互,并将 DOM 视为易于修改的黏土。如果我们能够实现这些模式,我们就能做到很多令人惊叹的事情,例如:
在运行时根据不同的用户角色调整我们的应用程序,并专注于满足他们的需求。
将我们的应用程序编写成函数。
实验和 A/B 测试更容易(顾名思义)。
我们的应用程序由普通函数组成,因此更容易进行测试。
你觉得怎么样?我很想了解其他人的想法和意见。
谢谢!
文章来源:https://dev.to/kmruiz/domain-driven-design-and-functions-pure-ui-components-29a7