|
/

技术实践第七期|业务型团队里的前端高效高质开发之路

官方小二
发布于 2 年前
22474
移动统计 U-App
其他

*更新于2022-3-4*

*作者:莫奇*

一、业务背景

我们团队是一个致力于线下广告营销的团队,我主要负责的是所有对外服务的户外广告营销产品的前端开发工作。我们团队不仅是一个侧重于营销结果的业务型团队,还是一个致力于开拓户外广告营销领域数智化蛮荒市场的创业型团队。主要的业务产品为“天攻智投”,基于友盟与集团的数据技术能力,面向线下营销场景,通过数据赋能,为企业提供从潜客挖掘、媒体智选、预算分配、智能监播到效果评估的户外数字营销平台。提供商圈洞察、精准圈人、媒体智选、预算分析、标准监播、效果及沉淀6大核心产品能力。已在快销、消费电子、企业推广、互联网、汽车、商场卖场等多个行业积累较多的成功案例。



经过几年的快速发展,业务已经从2018年开始立项时仅有致力于大客的天攻智投一个产品,发展到拥有面向中小商家的周边推产品以及面向中型户外媒体代理商的天攻智媒产品,中高低端客户群里全面覆盖的户外营销全链路产品。

业务型团队主要有几个非常鲜明的特点:

  1. 阶段泄洪式需求: 一年阶段式的大量开发需求集中提出
  2. 探索型业务变化快:业务在探索期策略方向多变化
  3. 开发周期短:业务方向确定之后给到研发开发时间非常短
  4. 开发人力资源少: 创新型业务,2018年业务最开始4个前端,自2019年开始至今基本只有我一个人负责所有对外的产品研发,工作中需要对接2-3名产品、4-5名服务端开发,前端人力资源紧张
  5. 高质量要求: 业务直接面向客户,对产品质量要求比较高

简单一句话就是人少活多还得保证在尽可能短的时间内高质量的交付开发需求。如何达到这样的要求?这里将主要通过在所在业务“天攻智投”产品发展过程中前端开发经验的总结分享一些在搭建高效高质的前端架构方面的实践经验。

为了能够使内容更好理解,将首先通过对开发过程中遇到的一些常见问题做一下总结,其次给出在解决问题的过程中总结出的一些保障前端项目高质高效的设计原则,再次按照设计原则分别阐述一些比较有代表性的实践经验。


二、前端项目质量和效率的复盘分析

得益于在业务型团队里一直做创业型的产品,随着产品业务的快速发展也经历了产品在由小型项目演变成大型复杂项目过程中的各种问题,并且在解决各种问题的同时业务发展也趋于成熟,此时就需要全面系统的考虑前端开发项目的拓展性、维护性、可靠性以及开发高效性等相关的事情。当然,关于项目的拓展性、维护性、可靠性在业务发展过程中也在考虑,并且随着业务的发展也在不断的优化。

本部分将结合自己过去的前端项目经验总结一下开发过程中前端遇到的一些常见问题。

2.1 代码业务逻辑耦合度高造成拓展性和维护性比较差

早期的代码业务逻辑耦合度高主要表现在几个方面:

  • 复杂表单逻辑基本只在一个表单组件文件中开发完成,没有做合理的组件拆分。业务复杂度高,在增加新需求和功能时也会变的非常的困难,可拓展性也不高。
  • 多个表格在一个页面中使用时,直接在页面组件中实现各个表格对应的业务逻辑,造成一个页面组件代码量非常大。业务中还存在多个页面中使用的表格有细微差别的情况,这种情况下也造成了重复代码非常多的问题。这也给后期表格字段修改的重构工作造成了一定的困难,维护性比较差。

2.2 项目目录结构与业务逻辑耦合度高可拓展性差


所在业务最开始设计的项目页面代码的目录结构是按照路由路径设计的,这样的设计在我们业务快速发展的后期就出现了几个比较典型的问题:

  • 业务功能变化需要调整路由,以路由为基础设计的目录结构需要重构,重构过程属于删除文件和新建文件,会造成代码版本管理无法追踪的问题。
  • 在各个页面中实现的组件无法统一管理,容易造成重复开发的问题
  • 有的组件存在一定通用性,会存在跨页面引用的情况,在后期代码重构时存在一定的潜在风险
  • 每一个路由需要按照规则建立对应的文件,在创建/编辑表单这样的场景下也必须要按照这样的规则建立文件,增加了无用的工作量,违背代码的复用性原则。

2.3 代码风格杂乱导致代码的可维护性以及可靠性差

在业务最开始时,需求一般都非常紧急,为了能够尽快的完成开发,对于CSS规范、二方依赖管理、三方依赖管理包括项目自身前端架构规范方面的建设没有过多关注,随着业务的快速发展,有些问题开始暴露出来:

  • CSS命名过于随意,造成很多不必要的问题。
  • 三方组件库的引入方式多样,增加项目学习成本和组件升级的风险系数,项目维护性和可靠性不高
  • 业务组件和通用组件没有做细分,缺乏业务组件的概念,不利于做跨业务组件通用化的处理
  • 缺乏枚举管理理念,代码维护性比较低。项目中对于枚举值,早期基本都是直接在逻辑中使用的硬编码。

2.4 高频低技术要求的操作耗时高影响开发效率

开发过程中,一般我们都会有一些没有技术含量的高频操作,这些操作在一个复杂的业务产品中也会变成一件非常耗时影响开发效率的事情。按照以往的经验,我们一般会存在这么几点高频低技术但耗时的操作:

  • 表格功能开发存在重复简单反复开发的情况
  • 组件初始化创建文件、填充代码也是一件非常简单但耗时的事情。

2.5 过度设计的技术方案降低了代码质量和开发效率

近些年代码可视化的风头兴起,集团内部也有很多团队在做这方面的事情,并且也出现了像UIPaaS这样的基础建设产品。我们团队很早的时候就基于乐高的vision做了一些可视化搭建相关的事情,并且当前所在业务对内部小二服务的部分业务也是基于自然的可视化服务搭建开发的。但是,每一种技术架构都有其适合的场景和不适合的场景,没有一种技术架构真的是万金油可以适合所有的场景。低代码也是如此,其在交互逻辑标准化、不要求高定制性的中小型业务场景的建设方面确实能够帮助业务快速落地,实现开发的高效率和降低开发成本。但是,对于具有一定的复杂度、业务逻辑存在高度定制化且对性能方面有要求的场景,低代码其实不仅不能够提高研发效率,反而由于新技术栈的引入和其自身对于定制化复杂场景的支持度低的特性,会降低开发效率。

在天攻智投一次大的版本更新时,引入了一整套的低代码逻辑,试图将非常复杂的表单逻辑使用低代码的方式进行实现。本来当时的想法是想要借助低代码的能力提高研发效率,结果最后完成开发的时间要比没有引入低代码时还要长。

需要特别说明的是这里拿低代码举例并不代表要全面否定低代码,低代码在很多场景下在研发提效方面表现非常好,只是在复杂表单或高定制化、强交互的业务场景下不太适合。

当然任何技术都不可能适应各种场景,换句话说没有最好的技术架构,只有适合自身业务当前发展的最好架构。低代码的引入就属于技术方案的过度设计,本身低代码是为了提效而生,但结果是不仅没有帮助业务提高开发效率,反而降低了开发效率同时标准化的可视化代码由于其阅读性非常的低、冗余代码非常多还拉低了代码的质量和产品的执行性能。

三、结合自身业务特点和行业经验制定保障高效高质开发技术架构的设计原则


所有原则都是吃过亏之后总结的经验教训。当在前端研发过程中趟过各种坑之后,发现对于一个大型复杂的应用系统而言设计模式和开发原则是必不可少的。不过通过对行业内应用比较多的设计原则的学习和研究,发现其实由于各个业务特点不一样、团队规模不一样等等原因,基本没有一种单一的原则可以适用于所有的团队,这也是为什么会有很多相关的开发设计原则存在。这一现象对于我们而言也一样,所以我们试图通过学习当前应用比较广泛的开发原则,结合所在业务团队的特点以及研发过程中的经验教训总结提炼出一套符合自身的开发设计原则。

目前大家参考比较多的设计原则主要有SOLID原则DRY(Don't Reapeat Yourself)原则KISS(Keep it Simple and Stupid)原则奥卡姆剃刀原理等。
通过对几个应用广泛的开发设计原则的学习,再结合当前业务前端开发过程中的经验教训,针对如何提高前端代码质量和开发效率,总结了一套自身的开发设计原则:

  1. 复杂逻辑简单化: 解决代码高耦合问题,提高代码维护性和拓展性。得益于在一个前端人力资源紧张的创业型业务中做开发,因为时间有限、精力有限才会花费大量的时间用于更好的抽象功能,简化复杂逻辑才能确保开发时的专注度,减少思维由于其他原因导致的被打扰时用来还原到专注研发过程的耗时。根据A diary study of task switching and interruptions研究表明当程序员工作被打断之后通常需要10-15分钟才会开始敲代码。需求沟通、排期确认等等相关工作我们在所难免,这些基本属于不可控的因素,那么我们只能专注于如何尽可能以优的方案将复杂逻辑简单化,然后增加自身编程专注度来提高研发效率。
  2. 重复逻辑通用化: 降低冗余代码,提高代码可维护性
  3. 通用功能工具化: 提炼通用能力,夯实业务前端基础建设,为多产品开发提供通用能力以提高开发效率
  4. 特殊需求定制化: 解耦特殊需求和业务主营业务需求,降低代码耦合度。在保证主要业务逻辑质量的情况下,由于特殊需求单独定制开发,也为需求的定制程度提供了更大的自由度。
  5. 代码规范标准化: 规范的标准化不仅可以提高代码的可读性,同时也会降低后期加入的同学的学习成本,使其能够更快的熟悉项目,加入到项目的开发工作中来,提高工作效率。
  6. 开发体验便利化: 好的开发体验,会帮助开发人员提高开发效率。工具善其事,必先利其器。开发体验的便利化,就像上战场时给士兵配备的一把好武器,能够更好地激发开发的开发效率。

四、高效高质开发准则的实践详情

根据以往的经验,每一个业务的技术架构不是从项目一开始就是最优的,后续可以不经过优化一直持续使用。我们的业务前端技术架构也是如此。下图是以天攻智投的项目发展为例说明所在业务前端架构的演化进程。



自18年项目开始,由于需求变化快且上线时间紧,前端主要核心工作在于优先保证需求可按时交付,功能可用。到20年初时业务方向趋于成熟,此时在开发新需求的同时,开始总结项目开发问题和痛点,也开始制定项目的优化策略和需要遵循的基本原则,有计划的、循序渐进的对项目进行重构。最终按照新的开发准则实现的前端架构如下图。



当前的前端架构具备几个主要的特点:

  1. 将基础组件细分成了基础模块和业务模块两种类型。
  2. 将页面细分成了五种具备一定最佳实践的类型。
  3. 通用能力进行了工具化的建设,最终形成了可以在业务内跨产品复用的二方依赖工具包。
  4. 具备路由管理、权限控制和缓存功能的应用容器管理能力。

每一种开发准则一般在实践中不会独立的出现,一个好的前端架构设计会同时满足多个准则。所以接下来的内容会按照开发准则分别展开介绍一下相应的、比较有代表性的实践。

4.1 复杂逻辑简单化的实践细节

4.1.1 定义表单逻辑拆分原则,简化表单组件业务逻辑

这里拿天攻智投的表单作为说明。天攻智投创建提案/方案的表单包含的业务逻辑非常的复杂,内部需要按照不同的平台、角色以及参数之间的关联关系实现不同的业务逻辑。


为了保证开发的代码质量以及维护性,在开发过程中,通过不断的抽象将复杂的逻辑进行分层和解耦以达到复杂度降维的目的,设计了一套表单开发的业务分层模式。核心的设计思想是通过对页面组件、复杂逻辑处理辅助函数、表单组件、字段组件进行明确的独立责任划分,达到业务逻辑复杂度层层简化的目的。
业务分层之后各层的主要职责:

  • 页面组件: 负责调用info2form将详情转成表单数据之后给表单设置值,当表单执行提交操作时调用form2info将表单值转成接口需要的格式发送给对应的创建或者更新接口。
  • 表单出入参数格式化辅助函数层: 主要有两个功能,一个是在编辑或者复制时将详情数据转化成表单组件需要的数据info2form,另一个是在提交表单请求接口时将表单组件的数据转化成接口需要的格式form2info。
  • 表单组件: 负责接收页面组件传入的value、执行提交、取消时对应的回调函数和各个字段组件的拼装以及校验。
  • 字段组件: 字段组件以切分表单的标签对应UI为基础单元拆分表单组件为各个独立的标准字段组件。采用最小化参数原则,从表单组件接收value参数,按照需要自身维护内部需要的接口数据。例如设备类型字段组件需要的设备类型列表就是组件自己通过接口获取的(为了避免接口的重复请求,接口请求的枚举值会统一做缓存处理)。字段组件拆分还有一个核心的原则是要尽可能的在保证解耦的情况下做到字段组件的开箱即用。


4.1.2 封装表格组件常用功能,简化表格组件开发逻辑

由于营销线业务属于数据类型产品,其中表格组件使用的场景非常多,而大量的表格可能只在操作项或者个别字段显示上会有稍微差别,为了保证功能模块最大的复用性,解决代码冗余的问题,基于antd对Table进行了二次封装。


如果我们要实现一个表格功能,首先需要传入筛选、排序和分页参数请求接口然后按照返回结果更新表格数据,当页面上有很多个表格时需要实现大量的类似代码并且还需要分别在引起参数变化的回调中分别加入数据获取更新的处理逻辑,非常的繁琐同时代码逻辑也会变的耦合性非常高,有没有一种方式可以简化这样的开发流程?答案是有。

在开发了大量的表格组件之后,通过对以往开发经验的总结,引入了参数自动化驱动的表格组件的实现设计模式。将antd的Table组件的实现进行了拆分然后再拼装。其核心设计逻辑为通过监控传入表格组件的属性变化,表格组件内部自动判断是否需要将表格内部状态和传入参数进行拼装执行传入组件的method方法进行表格组件的数据更新。当然实现的表格组件不仅仅简化了数据更新的机制,也丰富了其他的很多开发体验。



4.1.3 统一管理路由和接口配置,简化实现逻辑,降低维护成本

在项目中将所有路由的配置都在一个文件中以列表的形式进行配置,然后在容器初始化时按照当前用户权限从列表中筛选出拥有权限的路由进行动态的挂载。统一化的路由管理实现了react-router自身不具备的一些能力。在路由能力增强的同时,也避免了直接在业务逻辑中使用路由组件、实现路由逻辑造成的代码可读性和维护性低的问题。



为了使接口的添加、使用以及未来重构的便利性,业务中采用了统一的接口管理设计。与路由的设计模式一样,所有的接口都在唯一一个JS文件中管理,在应用初始化时会自动进行接口的API初始化操作。统一的接口管理通过提供下图展示的能力,提高了代码的维护性,不仅使得新增和变更接口变的非常的简单,同时也因为统一管理的过程中可以不断的完善、优化顶层的接口设计,在提高可靠性的同时也提高了工作效率。



4.2 重复逻辑通用化

4.2.1 统一枚举值的管理

在项目中会存在大量的枚举值的定义,早期这些枚举值直接硬编码在项目中使得后期重构和维护非常的困难。为了解决这样的问题,引入了枚举管理的模块。制定了枚举定义的规范。



4.2.2 通用化功能点权限控制

功能点权限的控制存在两种情况,一种是控制组件是否渲染、另一种是确定业务逻辑功能是否执行。
为了简化功能开发的代码逻辑,封装了功能点权限控制组件以及功能点权限辅助函数,保证适应多场景的灵活性。

4.2.3 统一埋点功能


针对项目不同的数据统计需求,封装数据统计函数。在需要数据统计的地方,只需要按照统计需求,调用相对应的统计函数即可。这样的设计不仅保证了功能实现的开发效率,而且也为切换统计第三方服务提供了可控性,同时也为网络重试统计合并提交全局参数配置提供了可能性。



4.3 通用功能工具化

4.3.1 业务组件库@ali/oui

将所在业务所有通用基础组件统一封装到@ali/oui组件库中去。所有通用组件都从@ali/oui引入,屏蔽之前对antd组件的引入问题,实现基础组件管理的统一化。在为设计规范的标准化提供基础的同时,也提供了组件跨产品复用的能力。

4.3.3 辅助函数库@ali/oui-utils和地图容器@ali/oui-map

将可以跨产品使用的辅助函数统一下沉到@ali/oui-utils中去,减少逻辑的重复开发的同时也为后续集成多产品开发经验不断完善辅助函数库提供了可能性。
户外营销相关的产品与地理位置有着非常强的关联关系,所以业务中存在基于地图开发的很多功能,为了使地图的常用公共服务能力、地图SDK版本控制、插件管理、懒加载等能力可以统一化管理,开发了地图容器管理能力@ali/oui-map,实现跨产品的地图基础能力的复用。

4.4 特殊需求定制化

4.4.1 天攻智投对外演示版本

21年时,天攻智投计划做一个对外演示版本。因为是演示版本,需要做很多定制化的处理,比如权限控制,主题设置、业务模块组织逻辑处理等。为了能够在开发时避免考虑是否对主体业务造成影响,为演示版本建立了专门的CDN项目。因为是在独立的项目中开发不用考虑改动会对线上业务造成影响,所以在短时间内就完成了对应的开发工作,研发效率非常的高。

4.5 代码规范标准化

4.5.1 CSS样式规范化

在CSS规范方面,前端应用比较广泛的是BEM命名规范,其核心的命名规则为[BLOCK]__[ELEMENT]––[MODIFIER]。Thoughtful CSS Architecture这篇文章对CSS的规范有着比较好的介绍,感兴趣的可以读一下。

其中[BLOCK]为命名空间。为了解决业务中CSS产生互相影响造成系统缺陷的问题,业务中引入了BEM的CSS原则。为了使得该原则更通用化和保准化,对[BLOCK]部分进行了细化,加入了组件类型前缀的命名空间。

  • 页面: .page-
  • 组件: .c-
  • 挂件: .w-
  • 表单: .form-
  • 表格:.table-
  • 状态判断: .is- 或 .has
  • 辅助类: .u-

除了命名规范之外,借助@ali/oui的建设,设计了一套实现多主题的方案。在业务中所有业务逻辑在实现样式时,需要引入业务中定义的less变量,间距、颜色、字体大小等等使用在业务less变量中定义的统一设计规范。



这里解决了一个之前实现多主题时的核心问题。

最早在实现组件的样式时直接在组件中引用antd的less主题变量,这在后期业务需要整体修改UI风格时造成了非常大的苦难。为了使得UI风格的改动更灵活高效,设定了所有业务组件必须统一引用业务中唯一的变量定义less,对其他主题包的集成和修改在统一的文件中修改,避免CSS变量随意定义造成后期维护和重构非常困难的问题。

4.5.2 JS代码规范化

接入了集团统一的代码规范监测工具,解决了冗余代码的部分问题,不过规范在命名规则以及逻辑处理规范方面的检测不能面面俱到。为此设计了JS的命名规则、逻辑处理代码拆分规则和将组件细分为Component和Widget两种。

JS的命名规范:

  • 页面 - [NameSpace]Page
  • 组件 - [NameSpace]Component
  • 控件 - [NameSpace]Widget
  • 表单 - [NameSpace]Form
  • 表格 - [NameSpace]Table
  • 状态管理 - [NameSpace]Store

同时规定每一个组件的文件目录结构中入口文件为index.jsx或者index.js,状态管理文件名为store.js以及样式文件必须为index.less这样标准化的目录。
标准化的文件结构使得每一个新人加入到项目中时,都可以通过简单的模式定位对应的代码逻辑,同时也为后续基于规范化做的代码格式化以及重构提供基础。



业务中逻辑功能的状态管理使用的是mobx。之前存在一种情况是前端在开发时有的逻辑写在主逻辑中,有的逻辑写在store中,而且什么逻辑应该放在哪里没有一个明确的规范。通过对以往开发经验的总结,确定了基本的逻辑代码拆分逻辑:



通常在项目开发中我们将所有独立的组件都划分为Component,常见的划分方式没有对业务组件和通用组件做区分,导致在一个产品中开发的组件很难进行通用组件能力的技术沉淀。为了能够保证在研发过程中可以提炼通用的组件能力以实现跨产品的复用性,减少重复开发的问题,提高工作效率,将组件划分为通用组件Component和挂件Widget。


Component设计原则为功能具备一定的通用性、内部不包含与业务相关的逻辑,最重要的一点是不能包含接口请求和应用全局状态调用

Widget的设计原则为尽可能的做到即插即用,采用最小参数原则,内部自己可以直接调用业务接口实现自身数据状态的维护和管理,类似于“集团小蜜客服”这样的功能组件。这样做的好处是可以最大化的减少重复开发的问题,例如无论天攻智投、天攻智媒还是周边推,这几款产品都需要在小二代运营平台内部可以切换商家,切换商家的组件需要展示可切换的商家列表,而且是全应用可见且需要跨产品实现该功能的,所以切换商家的组件是以WIdget的形式实现的,这样多个产品即可共享通用的能力。


4.5.3 项目文件结构治理

通过对历史的开发经验的总结,为了解决早期按照路由设计的嵌套式文件目录问题,将页面的文件目录改为扁平化的组织方式。早期嵌套式的文件目录虽然单独看某一个目录时简单明确,但是嵌套层级非常深时却给文件定位带来一定的困难,同时在组件引用时需要书写的文件路径也非常长,后期重构也会变的很苦难。
现在采用扁平化的文件目录组织页面,目录列表看似非常多,但实际上借助编辑器的检索能力结合当前指定的命名规范可以很快速的定位对应的代码文件。同时在配置路由时也因为需要设置的路径变的非常简单降低了后期重构的困难。



4.6 开发体验便利化

4.6.1 业务脚手架工具@ali/oui-cli

结合以往前端项目的一些最佳实践,针对开发过程中需要手动频繁创建文件和初始化代码的问题,开发了@ali/oui-cli命令行工具。使用命令行可以帮助开发快速的创建符合代码规范标准化的组件,减少高频低技术含量的工作耗时,提高开发效率。



4.6.2 坚持保持webpack打包脚本按产品的独立化

根据以往的经验以及看到过的例子,都证明将打包工具封装到脚手架中不是一个非常理想的方案。当前前端活跃的技术生态的驱动力来源于前端技术的开源生态激发了前端对于新鲜事物的不断探索的兴趣,重点则在于开放。而将打包工具封装到脚手架中无疑会使得项目开发人员丧失了在业务中应用和学习相关技术的可能性。另外,封装webpack到脚手架之后如何修改webpack对应的配置以适应各个项目的特殊需求也会成为一个非常困难的事情,所以在业务中做各种跨产品的提效工作时一直保持webpack打包的独立化。

五、总结与展望

经过在快速发展的业务中不断的优化,当前的前端技术架构虽然做到了高效开发,也具备了一定的拓展性、维护性和比较好的性能表现,但是也在有些方面需要完善,例如为了更进一步保证质量是否需要考虑自动化测试的事情以及是否可以考虑采用微应用的架构更进一步的简化项目的复杂度或者是否可以利用低代码实现部分标准化、配置化的业务逻辑的研发工作等等都需要做一些探索。

对于研发来讲,没有一种设计模式和技术架构可以一直适合业务的发展。作为一名开发人员要做的就是在开发过程中不断的挖掘问题和探索解决问题的方案。在一个需求多、变化快还要求质量高、客户体验好的业务型团队中,前端人员更应该将如何保障高质量的同时提高工作效率放在第一位。

六、参考文章

  • 开发团队的效率
  • 2019年中国上市户外媒体的营收统计报告
  • 设计原则
  • 设计模式之SOLID原则
  • DRY(Don't Reapeat Yourself)原则
  • KISS(Keep it Simple and Stupid)原则
  • 奥卡姆剃刀原理
  • Software Architecture : 5 Principles You Should Know
  • Architectural principles
  • SOLID Principles in React
  • Common web application architectures
  • The 7 most important software design patterns
  • Scalable JavaScript Application Architecture
  • A diary study of task switching and interruptions
  • Thoughtful CSS Architecture

2个回答
 
2 年前

👏👏👏👏👏👏👏👏👏👏

 
2 年前

👍👍👍👍👍👍👍👍👍👍

Loading ...
登录社区,分享你的经验