目前我所在的团队 wisdesign 正在逐步完成前端工程基础建设,我希望通过社区不断的去分享这个过程中遇到的关键问题,以及我们最终的解决方案,希望能得到大家的认可,共同讨论,共同成长。
我们当前面临着几个问题,也正是这些问题不断的催生出我们的想法和和如今已有的一些基础设施架构,我们的团队有几个前端、几个设计和几百个业务后端组合而成,需要完成上百个业务模块的开发与维护,对应的产生了一种合作模式,前端及设计部分转变成为了业务的支撑部门,大部分的需求由产品直接与后端研发人员对接,当后端开发人员无法完成前端部分的代码编写时,积极寻求前端的协助,这样的模式下,研发人员面临着几个难以解决的问题:
- 同时存在着如此多的业务团队共同维护一套交付给客户的庞大软件系统,需要思考如何有效的拆分工程,让每一个工程都独立开发调试及上线,减少团队之间的交流成本,同时需要思考,产品的交互规范的一致性怎么得到有效控制。
- 后端研发并非专业的前端,他们可以轻而易举的学习理解Javascript的逻辑编写,但是难以承担样式布局的编写。
- 对于PC端的CRUD页面对于后端研发来说是并不是什么难事,但是业务往往没有那么容易满足,我们的业务所涉及的用户群体除了在PC端完成工作外,用户还需要快速的在手机端完成便捷任务,也就意味着相同的功能还需要移植到移动端,对于移动端的编写也会让后端研发感到苦恼。
部门之间的无缝协作整合
为了解决多个团队共同协作完成一整个项目的研发,思考一下现在前端社区中有哪些技术可以完成,显而易见的是“微前端”这个方案正是可以解决我们的困扰的一种技术方案,我们调研了社区上的大部分的微前端技术,它们着重于解决一些古老项目的整合与升级,以及跨技术栈项目的整合,这些对于我们来说并非是必须的,我们可以保持一套技术体系,对于部分历史项目为了保持整体规范一致性,有决心也愿意花时间去重构迭代。社区的微前端有一个共同特点,让我们有些在意,虽然子应用已经实现了独立研发调试,但是当我们需要整合子应用时,需要尝试与壳应用进行沟通注册,并且同时发布子应用项目与父应用项目才能完成整合,这意味着维护着父应用的前端团队时常需要被其他部门拉会沟通上线方式,我认为这不是一个很良好的模式,特别是经常需要深夜上线的情况,最终我们决定构造自己的微前端架构体系,解决我们所面临的问题。
我们整体的微前端基石采用了Webpack Module Federation方案,但有别于现有的方案我们魔改了Module Federation插件,抹去了remotes的注册步骤,项目之间无论是页面的整合嵌套或者是公共资源在项目之间的共享,不在需要在编译运行时注册对应的引用项目地址,省去了沟通协调即可完成共享。这个时候当我们需要在业务项目(app)中使用其他团队维护的公共资源项目(public_components)时,一个简单的代码案例如下:
import WareSelect from "remote:public_components/wareSelect"
export default function WarePage() {
return (
<WareSelect />
)
}
通过这个方式我们可以像维护项目自身组件一样简单的以项目的形式维护我们的组件库,省去了npm包管理的繁琐发布流程,我认为这个非常的棒,省去了很多的公共资源维护心智负担。
解决了公共资源的共享问题,接下来该思考的是如何解决跨项目页面的整合问题,由于我们的页面与社区中其他框架一样采用的文件系统映射路由的方式,因此我们考虑通过访问的URL地址着手解决,将URL划分为如下几个结构:
https://host#[layout]/[application]/route/route
为了理解整个URL的构成,我们需要先了解两个概念:
- Layout,应用的公共部分,用于承载应用菜单,面包屑,用户信息,语言切换等这些页面间共同拥有的部分,同时一个应用可以同时支持实现多种Layout,且支持在线根据场景切换,比如登陆页,通常不需要任何布局,再比如当你的应用嵌套进其他应用时,为了更好的融合,时常会为了该场景单独实现一种布局。
- Application,表示当前页面所属的应用工程,时常与应用名称保持一致,后续的路由地址都指向了该应用。
按照上面的路由方式,发现,整合一个完整的项目变得容易,各个团队之间实现了真正的分离,不再需要繁琐的沟通协调,我们需要的只是维护菜单,建立菜单权限,公共项目团队统一维护公共资源及文档,提供给各个业务部门消费使用即可。
产品交互规范的一致性,解决后端无法上手样式编写的问题
要真正解决这个问题是一个很难的难题,我们能想到方案是通过向业务部门输出规范一致的组件库,但是现在社区上已经有如此多的组件库,在不同的研发和产品手中,实现出来的产品规范千差万别,这并不能解决我们的问题,我们不能依赖于人工的规范统一,我们需要进一步抽象组件,既然后端研发不会写样式,那我们就让后端研发无样式可写,我们抽象了布局组件,通过标签的快速嵌套即可完成满足我们规范的布局及交互,这是一个难以用语言阐述明白的理论,举一个简单的例子。
当产品经理需要向研发阐述这样一个需求时“客户需要一个页面,页面中间显示一个采购物品列表,展示采购人员的名称,采购时间,采购物品,同时在表格上方有一个过滤用的搜索表单,可以基于人员名称,采购时间和采购物品进行快速搜索,同时需要在页面的左上角显示采购物品清单的标题,并且在页面的右侧放一个创建采购物品的按钮,及批量导入的按钮,同时创建按钮需要着重显示”,通过这一段描述,我们发现,一个完整的需求在社区的组件库中即要描述每一个元素,小到一个按钮,大到一个列表的布局方式及位置,同时还需要描述产品功能。
这时候我们开始思考,如何让产品专注于功能需求,而不在关心布局规范及样式,比如一个展示列表页该是怎样的一个布局,又或是按钮什么情况下需要高亮,这个时候,一个需求的描述开始逐渐发生变化,“客户需要一个页面(Page),页面的标题是采购物品清单(Table),展示采购人员名称(Cell),采购时间(Cell),采购物品(Cell),同时支持采购人员(Item),采购时间(Item)和采购物品(Item)的快速搜索(Form),还需要支持创建采购物品(Button),批量导入功能(Button)。” 神奇的发现,产品经理不再需要描述布局方式,不再需要帮助研发或者设计构建产品的原型,只需要专注于描述所需的功能即可。
产品经理省去了原型规范交互的描述,并不代表规范就消失了,或者随便研发如何编写,只是我们将产品的规则提升了一个新的维度,由全局规范及组件规范来承载。研发所书写的代码表达着上述产品所描述的功能。比如上面的列子所映射的代码
export default function PurchaseManagePage() {
return (
<Page title="采购物品清单">
<Actions>
<Button text="批量导入采购单" />
<Button text="创建采购单" />
</Actions>
<Form type="search">
<Item label="采购人员" name="name" as={<Input />} />
<Item label="采购时间" name="time" as={<DatePicker />} />
<Item label="采购物品" name="ware" as={<Input />} />
</Form>
<Table>
<Cell label="采购人员">{rowData.name}</Cell>
<Cell label="采购时间">{rowData.time}</Cell>
<Cell label="采购物品">{rowData.ware}</Cell>
</Table>
</Page>
)
}
通过这样的形式,我们逐渐的发现问题得到了很好的解决,研发也不再需要关心样式,基本上告别了样式的编写,同时也解决了多个团队的产品一致性的问题,当我们需要不断完善我们产品的交互时,需要做的不再是聚焦于一个个的页面的细节调整与对齐,仅需要从调整提取的全局规则及组件规则方面着手,这期间我们做了很多的事情,包括组件间的嵌套规则限制及规范定制,这是一个需要长时间累计才能完成的目标。
让一个地方书写的代码跑在多个端
为了解决最后一个难题,相同的功能,需要同时在PC端和移动端使用,我们不希望我们的应用达到只是“长得像”移动端这样表面的形式,我们所追求的是内在的真正的体验式跨端的移动端交互,比如大到布局的移动端完全适配,小到组件的如PC的Select在移动端应转变为功能相同的Picker组件,这不再是通过简单响应式适配而实现,我们需要建立一种跨端机制,也可以说是跨端资源的加载机制。
我们对浮现于研发眼前的标签所涉及的资源映射为两套完全独立的资源,一套完成PC的组件实现,一套完成移动端的组件实现,两套组件库相对研发开放一套共同的标签定义,由我们的工程体系实现在浏览器端的运行时切换资源加载的机制,达到在一个地方写一套相同的代码,跑在多个端的能力。由于底层的两套组件库是独立的,它们就像自由的小鸟,只需要满足不要飞出队伍的范围内都可以自由翱翔一样灵活多变。这对于业务层研发是无感的,因为它们一直只是维护着一套代码,满足着用户不同端的访问需求。
同样是,我们发现这样的模式不再是局限于跨端组件库的实现,它可以解决我们更多的问题,比如多主题及多语言切换时,仅加载当前的资源,不在同时加载多套资源,导致资源资源浪费的问题。
结语
通过简短的语言文字并不能很好的表达出来,我们所做的尝试和努力,更多的是向大家传达我们在解决面临的问题所思考的东西,如果我所写的东西能够帮助到你,并且你希望了解更多的设计和编码细节,欢迎关注我们 wisdesign,让更多的人看见,也是帮助更多希望学习和讨论的人,未来的某个时间点,我们将会开源出来,同时我们希望也能做更多对社区有帮助的事情。