自动化回归测试平台 AREX 前端架构演变史 —— Tabs 动态组件设计
AREX (http://arextest.com/)是一款开源的基于真实请求与数据的自动化回归测试平台,利用 Java Agent 技术与比对技术,通过流量录制回放能力实现快速有效的回归测试。同时提供了接口测试、接口比对测试等丰富的自动化测试功能。
在这个系列中,我们将会介绍 AREX 前端架构的演变过程,以及在演变过程中遇到的问题和解决方案,一来作为开发过程的经验分享,二来方便大家对 AREX 源码的理解以及二次开发。
Tabs 动态组件设计
Tabs organize content across different screens, data sets, and other interactions —— Material Design
Tab 组件在前端开发中有着广泛的应用场景,它能够满足各种不同的需求,如数据展示、导航菜单、表单填写、产品分类、菜单展示、图片幻灯片等多种场景。在 AREX 中也有大量使用 Tabs 组件的场景,其中最具代表性的是 AREX 主工作区。在这篇文章中,我将会介绍 AREX 主工作区中 Tabs 动态组件的设计思路和实现迭代过程。
![](https://static001.geekbang.org/infoq/d4/d4fc7e06a9795e752af30c990a2ee482.png)
主工作区是 AREX 的核心功能区域,用户在这里完成 API 调试、录制用例回放、环境管理、应用配置等一系列任务。由于提供的功能众多,用户使用流程非线性,经常需要在多个功能模块之间切换,为了提供良好的用户体验和高效的操作方式,AREX 主工作区采用了 Tab 组件来组织和展示主工作区的不同功能页面模块。
用户在主工作区借助 Tabs 快速地切换到所需的功能页面,同时 Tabs 在切换时对功能页面进行缓存,避免了频繁地重新初始化功能页面的操作,提高了用户的体验和使用效率。这看起来是一个理所应当且很简单的功能,毕竟 Tabs 本身的功能已在 UI 框架中实现,但是在实际的过程中还是碰到了一些问题。
1.0 —— 原始的条件渲染
在 AREX 的早期版本中,Tabs 组件采用的是 Ant Design 4.23.0 之前的 JSX 拼接写法。Tabs 组件实现的简化代码如下:
主工作区中的 panes 负责维护各个功能页面的种类、名称、页面初始化参数等数据,并由全局状态进行管理。当新增或关闭功能页面时,触发 panes 的更新操作。
主工作区的 Tabs.TabPane 组件通过条件渲染来实现。在对 panes 进行遍历时,根据每个 pane 的 paneType 属性匹配到相应的页面组件,并进行渲染。这种实现方式的优点在于简单直接,通过条件判断能够实现页面的动态渲染。然而,这种方式也存在一些缺点,例如大量的条件判断语句降低了代码的可读性和优雅性。同时,可扩展性也较差,当需要新增一个功能页面时,需要修改 Tabs 组件的代码,违反了开闭原则。
因此,为了解决这些问题,我们自然而然地想到需要一种更加通用的实现方式,以适配未来可能出现的更多未知的功能页面。
2.0 —— 动态组件渲染
由于 Tabs 组件中 TabPane 所对应的实际组件是不确定的,它随着用户的操作而动态变化,因此我们想到将 TabPane 由原先的条件渲染组件改为动态组件。通过动态组件的方式,我们可以根据配置信息动态地加载和渲染功能页面,实现了更高的可扩展性和灵活性。这样一来,新增功能页面时只需配置相应的参数,而无需修改 Tabs 组件的代码,大大减少了维护和扩展的成本。这种改进不仅提升了代码的可读性和优雅性,也为未来的功能扩展奠定了坚实的基础。
在前端开发中,动态组件是一种广泛应用的技术,它可以帮助我们实现高度灵活和可复用的组件,并满足动态渲染组件的需求。动态组件的概念是基于组件化开发的理念,将页面拆分为独立的可重用单元,通过动态加载和渲染这些组件,实现灵活的页面构建和交互。
在 Vue 框架中,我们可以通过 <component v-bind:is="componentName"></component>
的方式来实现动态组件。其中,componentName
可以是一个变量,根据需要动态指定不同的组件名称,从而实现组件的动态加载和渲染。
而在 React 框架中,我们可以通过声明一个组件的变量,然后将这个变量作为 JSX 的标签名来实现动态组件。这种方式允许我们根据需要在运行时选择不同的组件进行渲染。此外,我们还可以使用 React.createElement() 方法来动态创建并渲染组件。
在 AREX 升级适配 Ant Design 5.0 的重构版本中,我们使用了第二种方式来实现动态组件,同时使用了 Ant Design 提供的新的 Tabs 简写方式,用 item 有更好的性能和更方便的数据组织方式。优化后的动态 Tabs 组件的简化代码如下:
在这个方案中,我们将功能页面的种类和组件映射逻辑从 Tabs 组件中抽离出来,放到了一个单独的文件中,这样做的好处是将 Tabs 组件的职责进行了拆分,使得 Tabs 组件内部不再关注具体被注册的功能页面组件,同时也方便了功能页面的扩展。
除此之外还有两个细节应当注意,其一是在 Panes.ts 中定义了 ExtraPanes
对象,用于对外暴露扩展组件的配置,供对该项目的二次开发者使用,提供专用的配置空间可以实现二次开发代码与源代码的隔离,防止二次开发在与源代码同步时发生代码冲突,这样的配置空间隔离在 AREX 的设计中也有多处体现;其二是在动态组件外使用了 ErrorBoundary 组件对其包裹,用于捕获因非法的 paneType 造成的动态组件渲染失败和动态组件内部的错误,避免导致整个页面的崩溃。
3.0 —— 组件注册管理
在上个方案中我们已经提供了一个专用的配置对象,以解决动态组件的拓展性问题。但是局限性仍然存在,我们希望能够提供一个更加通用、灵活的组件注册管理方案,以摆脱只能在 Panes.ts
文件的 ExtraPanes
中进行配置功能组件的限制。
这个问题在 AREX monorepo 重构过程中显得尤为重要,原因是在 monorepo 版本中,AREX 被拆分成 arex-core
公用纯函数组件包和 arex
业务逻辑包,负责渲染主工作区的 Tabs 组件被封装到 arex-core
包中,而 tabsItems
在遍历时需要用到的映射配置 Panes
在两个包中均有定义,集中化的配置方式已经不能满足需求,我们需要一种可以分布式、多次进行的组件注册管理方案。
为了解决这个问题,我们设计了 ArexPanesManager
容器,用于管理功能页面组件的注册和获取,其简化代码如下:
借助该容器提供的 ArexPaneManager.registerPanes()
方法,可以实现在不同包中分别注册功能页面组件,最终在 arex-core
包中的 Tabs 组件借助 ArexPaneManager.getPaneByType()
方法统一获取完整的功能页面组件的映射配置。
值得一提的是,为了统一所有功能页面组件 Pane 的规范,我们设计了标准的 ArexPane
类型,所有出现在主工作区 Tabs 下的功能页面组件都应当继承该类型。为此我们提供了 ArexPaneFC
类型和 createArexPane()
方法,所有的页面功能组件先由 ArexPaneFC
定义,再经 createArexPane()
方法封装后,便得到可注册至 ArexPaneManager
进行维护管理的 ArexPane
功能页面组件。相关的类型定义和方法简化代码如下:
经过三个版本的演变,我们最终实现了一个通用的、分布式的功能页面组件注册管理方案,该方案在 AREX monorepo 重构中得到了应用,也为 AREX 的二次开发提供了更多的可能性和便利性。代码的封装性和扩展性一直是开发过程中需要考虑的重要因素,我们在 AREX 的开发过程中也一直在思考如何提高代码的封装性和扩展性,希望能够为二次开发者提供更好的开发体验。
AREX 文档:arextest.com/zh-Hans/doc…
AREX 官网:arextest.com/
AREX GitHub:github.com/arextest
AREX 官方 QQ 交流群:656108079
评论