前端效率提升实践之路

简介: 在一个B端前端项目中,开发团队面临开发效率低、交付质量和可维护性差的问题。为了解决这些问题,他们以“提效”为主题,展开了项目治理。首先,他们优化了发布和编译过程,通过更换包管理工具、减少不必要的包、使用缓存策略等方法,显著缩短了发布和编译时间。其次,团队致力于沉淀可复用物料,创建了高度配置化的组件,通过VSCode插件助手自动化配置,提高了代码复用性和开发效率。此外,他们还改进了研发流程,制定了前端、后端和产品的规范,以减少沟通成本和提高接口质量。通过这些措施,团队成功提升了开发效率,并降低了代码维护成本。

背景

去年接手了一个B端的前端项目,与另外两名同学组成了一个小开发团队来承接项目。

改项目虽然属于新业务,但是因为框架是从另外一个老项目中fork出来的,存在很多历史问题。

开发两个多月的开发之后,团队成员普遍感觉开发效率低, 交付质量差,可维护性差。

这样下去,不仅开发效率难以提升,且代码维护成本会越来越高。

因此,我们以“提效”的专题,展开了对改项目的治理。


问题

“提效”是一个较大的目标,如何达到这个目标,则需要具体的实现路径。

我们将"提效"拆分为以下一些实现路径:

  1. 发布和编译提效

   整个上线发布的时长达到了30分钟左右,对于我们这个不算很大的项目来说,这个数字有点夸张的。

      在开发过程中,本地编译时间过长,也会非常影响开发效率和开发体验。

我们将这一项作为首要,因为这一项具有一下特点:

a. 数据好衡量

b. 与开发关系最密切

c. 改造成本低(因为要社区现有的一些方案)

  1. 沉淀可复用物料

可复用物料是几乎每个前端团队都会做的事情,对前端的提效也是明显的。

对于一些较好的社区组件,比如Antd,已经有了非常丰富的生态和物料是可以直接使用的。但是由于我司使用的自己的组件库,生态上有所不足,因此我们需要根据自己的业务和现有的组件库来做沉淀一些新的物料。

除了物料,我们还建设了vscode插件,来生成模板代码,降低物料的使用难度。

  1. 研发流程提效

从业务交付的角度看,前端的开发不是孤立的,是与产品,后端,QA一起协作的。减少跨团队的协作成本,也是我们提效的一部分。

实施

发布和编译

发布的流水线包含了git,yarn,lint, test,build,publish等节点。从时长上看,主要瓶颈在于yarn install和build上,我们分步解决问题。

Install

install的时间过长,可以从四个维度来优化

  1. 包管理工具升级

我们将管理工具切换到了社区号称最快的pnpm。pnpm具有两个主要特点,一个是快,另一个是使用硬链接减少了包的重复安装。pnpm的优点可以在官网上查看。

  1. 减少包的数量

     使用npm-check工具,去掉无效依赖。同时将一些重复作用的包去掉,如“qs”和"query-string",一些大包切换成小包,如"monentjs"切换成"dayjs"等。

     最终将包从2G缩小到700M(当然这也有pnpm的效果)

  1. 使用缓存

我们的发布流水线支持了使用包缓存,但是需要自己写插件。我们在非prod环境上增加了缓存插件,使得命中缓存之后,仅需2秒就可以完成安装。

  1. 依赖文件管理

为了防止依赖的劣化,我们在流水线中加入了一个新的插件。它可以对比当前发布的分支和上次发布的分支,进行packages.json的对比,如果有包的更新,增删,则会在公司的IM上发起审批和提醒。

经过以上的优化,我们的包安装时间,从300多秒,减少到了20秒,如果缓存命中了,则减少到2秒。


Build

我们的项目是历史项目fork的,采用了公司内部的工具,底层是webpack3.X。我们查看了packgeslock文件,发现这个工具为了做兼容和监控,安装了很多不必要的包,启动了一些后台的服务上报数据。这些对性能都会有一定的影响。

经过调研之后,我们放弃了使用vite 和webpack5。

vite当时在社区中很火,但是我们尝试迁移了一下发现如下问题:

a. vite启动很快,但是白屏时间很久。

b. vite的迁移成本较高,而且由于插件系统与webpack 不兼容,所以有些业务特用的插件如SSO登录,就可能要后面手写。

c. vite线上线下的打包底层不一样,有一定的风险。

相比之下,webpack5.X是较好的方案,而且由于webpack5采用了缓存策略,性能上大有提升。

但我们最终还是没有选择webpack,而是将目光投向了2023年发布的rspack。

基于以下原因

a. webpack 基于Node, 在编译这种计算密集的场景下,node存在天然的缺陷,随着代码量的增多,webpack最终还是会陷入到性能问题中,且难以继续提升。而相比之下,rspack基于rust,则可以很好的规避这些问题。

b. rspack经历了字节跳动自己的项目历练,其编译核心swc更是早在几年前就被next使用了,也是较为可靠的。

c. rspack在插件系统上,兼容了webpack,迁移成本低。

d. rspack 正在推广阶段,issue响应很快。


在迁移过程中发现,rspack作为新开源的方案,确实出现了一些问题,但好在官方响应比较快,最终得以解决。

完成迁移之后,我们的编译时间从16分钟,缩短到了2分钟,效果显著。


注意

底层工具的迁移,涉及的范围非常广,因此上线的风险是非常高的。一定要协调好产品,测试,确定上线的时间,提前做好回滚方案和测试方案。

沉淀可复用物料

高频组件沉淀

高频组件类型

对于B端业务来说,高频的组件主要是几个

  1. Table,主要的场景是展示和编辑
  2. Form,主要的场景是CRUD的搜索和编辑
  3. Page,主要的场景是新增/编辑/详情页面的布局
  4. Description,主要的场景是详情

做组件主要分两个方向,第一是做交互和功能上的增强,第二是满足业务上定制化的需求。

对于交互和业务上的内容,我们本文不做赘述,主要讲组设计上的考量。

可配置化

我们以一个典型的CRUD页面来说,一个CRUD主要包含了几个部分:

  1. 顶部的搜索栏
  2. 底部的表格
  3. 点击查看时跳转到详情页或者详情弹窗
  4. 点击编辑时跳转到编辑页或者编辑弹窗

我们会发现,这几个组件有非常多的相同字段,比如对于一个查询用户的CRUD来说,在这4个部分可能都要去展示"名字"这个字段。

在antd中,我们需要这样实现:

  1. 搜索栏

image.png

  1. Table

image.png

  1. Description

image.png

  1. 同1

我们需要对一个字段进行三次配置。这显然过于麻烦。

一方面,我们让组件支持了可配置化,通过配置Items数组参数,即可完成对页面字段的映射。对于表单常用的组件,比如筛选,文本输入,数组输入,时间选择等,我们做内置的支持,用户仅需配置字段类型即可。

另一方面,我们将这三个组件的字段配置进行统一,抹平差异:

image.png

这样的话,我们仅需一个配置数组,再根据场景进行过滤和自定义render即可完成一个CRUD的配置:

image.png

配置化的程度问题

在可配置化的路径上,一个我认为的邪路,就是做成watch:['name'] 这样的方式。我们在实现的时候,也参考了Xrender这样的开源方案,经过使用,我们还是摒弃了这种大量schema的方案。

我认为schema存在以下问题:

  1. 容易写很长很复杂,难以维护
  2. schema 无法利用TS类型提示,一旦出问题难以排查
  3. schem处理联动的方式不灵活
  4. 上手难度高,且投入产出比较低

因此,我们没有采用全量配置化的方式,而是使用了render函数,让用户处理自定义和联动的场景。

为了增强组件的可复用程度,我们协调了多团队的UI和产品进行设计,对组件进行文档和demo的完善,这样组件建设之后可以被更多人使用,扩大成果。

VScode插件助手

自动化配置

在日常开发中,页面显示什么,往往跟数据结构是相关的。如果我们知道了数据结构,那么就可以根据数据结果,生成一个配置化的数组。

由于我们的业务复杂,接口返回的数据结构也较为复杂,手动建模也是很耗时的事情。

因此,我们做了以下功能:

  1. 根据YAPI的接口返回的JSON,生成TS类型
  2. 用户在代码中选择TS类型或者接口时,可以调用插件生成常用组件的配置,如Form,Table等

有了以上的功能,我们自然而然的想到,如果我们内置一些模板,动态生产它的配置岂不是效果更好?


模板带来的好处
提效

因此,我们将一些典型场景,如CRUD,可编辑的Table,多From的Page等场景,写成插件内置的模板。当用户生成配置之后,将这些配置动态写入到模板中,同时生成mock数据。这样,用户只需根据后端YAPI的文档,就能秒生成一个可以运行,可以调试的页面。

这样的效果是提效的一环,同时也是代码质量的一环。

灵活

我们也避免了把组件做的过大。

比如还是CRUD来讲,通过模板,我们就可以把搜索栏和表格,分页逻辑,页面状态等放入模板中,交给用户管理。而如果不用模板,那很可能需要一个将其结合起来的叫ProCurd的组件了,组件自己来维护状态和一些搜索栏重置,提交的逻辑。这样的话,灵活性就欠缺了。

标准实践

业务开发时,时常会遇到这样的页面,单一页面内,放置了很多个可编辑的Form。

image.png

这种场景下,由于提交前校验的存在,很多开发都不得不用一个Form将所有card里的form包起来。而我认为这种方式会把很多相对独立的逻辑糅合到一起。因此,在组件开发时,我们就给这些组件

提供了非常多的ref方法,既方便用户在逻辑上进行拆分,也方便用户在必要时进行数据之间的交互和联动。如这样一个复杂的页面,我们的index文件,非常的简洁: image.png

各个Card的业务逻辑,在CustomForm中单独实现即可。



研发流程提效

  1. 前端的流程和规范

这主要包含了一些代码规范,命名规范,技术设计等规范。

流程则包含,组件库升级,代码发布,回滚,缺陷复盘等。

  1. 后端的规范

这主要包含前后端的接口规范。我们对于接口的方法,返回的错误码定义,数据结构定义,通用字段定义,字段类型定义,来提升我们的接口质量,降低前后端因为接口改动来回沟通的成本。

  1. 产品的规范

这主要包含交互规范,需求准入规范,需要排期规范等约定

  1. QA的规范

这主要包含用例评审规范,转测规范,定期的缺陷复盘等


长期的治理

静态代码分析

为了防止代码劣化,我们开发了一个静态分析工具。一方面,可以分析出无效的代码文件,在仓库中进行清理。另一方面,可以根据变动的文件,向上搜寻,查出可能影响到的前端路由页面,便于进行回测。

其实现原理如下:

    image.png

这里面的技术点主要是两个

  1. 路由算法分析

我们采用的ReactRoute来进行的配置的,写法相对固定,我们可以根据关键字进行分析

a. 首先遍历.router文件夹,读取代码。并解析出包含有path和component的对象,并找出component对应的导入。(这里如果要解决上述几种变量重命名的问题,就要使用到babel的scope,这个就复杂了)
    b. 读取ModuleLoader中的导出源码,并匹配关键字AsyncModuleLoader,找到组件对用的文件路径。
       由于从代码中读取的文件路面,在匹配路径的过程中,需要根据项目打包工具的alias,extensions做同等的配置。这样才能准确的将相对路径转换为绝对路径。

  1.   导入导出分析

引用组件,一般有四种写法

image.png

     根据使用的频率,我们支持了第一种和第三种分析。对于依赖图的分析并不复杂,我们根据路由的入口文件,使用深度遍历,进行递归即可。

领域建模

DDD的开发方式,在后端较为流行,包含的内容也较多。考虑到我们业务的复杂性,我们在前端也借鉴了DDD的一部分思想,就是与后端一样进行领域建模。

比如对于一个常见的供应链系统来说:

我们把采购的整个链路,叫做采购域。在这个域里,相同概念的东西保持一致。

一瓶饮料,在商品域和在采购域是不同的模型。

比如在商品域,这瓶饮料要包含名字,条码,sku。

而在采购域,这瓶饮料就仅仅需要Sku和名字。

这样的好处是,每个域的数据是精准的,易于理解的,认知成本也会降低。


同时我们将前端数据分为了两层:

image.png

    这样的好处是,用户逻辑和业务逻辑进行解耦。比如用户在页面操作的是一个字符串,但是提交到后端时,需要将其分割成一个数组,那么这个逻辑就可以放在Modal层去做。  

但是数据的来回转换是非常累人的,因此我们采用了AOP的方式来实现转换。

image.png


总结

经过半年多的建设,我们的提效工具已经相对完善了很多,整体统计下来,约给前端带了40%的提效。

目录
相关文章
|
2天前
|
资源调度 前端开发 JavaScript
前端工程化实践:Monorepo与Lerna管理
**前端工程化中,Monorepo和Lerna用于大型项目管理。Monorepo集纳所有项目,便于代码共享、版本控制和CI/CD。Lerna是Monorepo工具,管理多npm包,支持独立或共享版本。安装Lerna用`npm install --save-dev lerna`,初始化后可创建、管理包,通过`lerna bootstrap`、`lerna add`、`lerna publish`等命令协同工作。Lerna配置可在`lerna.json`,与CI/CD工具集成实现自动化。
4 0
|
21天前
|
敏捷开发 设计模式 前端开发
实践总结|前端架构设计的一点考究
本文总结了作者在日常/大促业务的“敏捷”开发过程中产生的疑惑,并尝试做出思考得到一些解决思路和方案。在前端开发和实践过程中,梳理了一些简单设计方案可以缓解当时 “头疼” 的几个敏捷迭代问题,并实践在项目迭代中。
|
27天前
|
缓存 前端开发 JavaScript
微前端框架开发实践的体验报告
微前端架构作为一种解决方案,通过将应用拆分成更小、更易于管理的子应用来提高开发效率和应用性能。本文将分享我在开发微前端框架过程中遇到的问题、解决思路以及具体方案。通过本次微前端框架的开发实践,我们成功实现了应用的解耦和性能的提升。关键点包括跨域问题的解决、路由分发的实现、沙箱和样式隔离的技术应用、通信机制的构建以及性能优化策略的采用。我们的成果是建立了一个高效、可扩展、易于维护的微前端架构。同时,我们也认识到了微前端架构的复杂性,以及在实施过程中需要考虑的诸多细节问题。
68 0
|
1月前
|
前端开发 Java Go
从前端到后端:构建现代化Web应用的技术实践
本文将介绍如何通过前端和后端技术相结合,构建现代化Web应用的技术实践。我们将探讨前端开发、后端架构以及多种编程语言(如Java、Python、C、PHP、Go)在构建高效、可扩展的Web应用中的应用。
|
1月前
|
缓存 算法 前端开发
前端开发者必知的缓存淘汰策略:LRU算法解析与实践
前端开发者必知的缓存淘汰策略:LRU算法解析与实践
|
1月前
|
Web App开发 前端开发 JavaScript
构建跨浏览器兼容的前端应用:技术实践与挑战
【5月更文挑战第16天】构建跨浏览器兼容的前端应用是应对浏览器差异和多样性的挑战。使用现代框架(如React、Vue)能自动转换代码,编写可移植的Web标准代码,结合浏览器兼容性测试工具和Polyfill解决旧浏览器支持问题。关注浏览器更新,应对性能、API差异和样式问题,采用渐进增强、条件判断和CSS Reset策略确保应用在各种浏览器上运行良好。
|
1月前
|
缓存 监控 前端开发
使用Angular进行企业级前端开发:实践与探索
【5月更文挑战第14天】本文探讨了使用Angular进行企业级前端开发的实践与技术要点。Angular,Google的开源JavaScript框架,以其组件化设计、响应式开发和依赖注入等特点成为首选。文章涵盖项目初始化、组件化、状态管理、路由、性能优化和测试部署等方面,强调了在企业级应用中如何利用Angular构建高效、稳定和可维护的前端解决方案。
|
1月前
|
前端开发 JavaScript API
深入理解前端开发:从基础到实践
深入理解前端开发:从基础到实践
|
1月前
|
移动开发 前端开发 UED
【专栏:HTML与CSS前端技术趋势篇】渐进式增强与优雅降级在前端开发中的实践
【4月更文挑战第30天】前端开发中的渐进式增强和优雅降级是确保跨浏览器、跨设备良好用户体验的关键策略。渐进式增强是从基础功能开始,逐步增加高级特性,保证所有用户能访问基本内容;而优雅降级则是从完整版本出发,向下兼容,确保低版本浏览器仍能使用基本功能。实践中,遵循HTML5/CSS3规范,使用流式布局和响应式设计,检测浏览器特性,并提供备选方案,都是实现这两种策略的有效方法。选择合适策略优化网站,提升用户体验。
|
1月前
|
Dart 前端开发 测试技术
【Flutter前端技术开发专栏】Flutter开发中的代码质量与重构实践
【4月更文挑战第30天】随着Flutter在跨平台开发的普及,保证代码质量成为开发者关注的重点。优质代码能确保应用性能与稳定性,提高开发效率。关键策略包括遵循最佳实践,编写可读性强的代码,实施代码审查和自动化测试。重构实践在项目扩展时尤为重要,适时重构能优化结构,降低维护成本。开发者应重视代码质量和重构,以促进项目成功。
【Flutter前端技术开发专栏】Flutter开发中的代码质量与重构实践

热门文章

最新文章