当前位置: 首页 > news >正文

Monorepo or 物料市场?结合工作实际情况对公司现有前端体系的思考

前言

去年年中基于若依vue前端框架进行了改造,加上后端的配合,我写了一套脚手架和项目中后台模板。中后台模板中包含了许多基础代码,比如登录/注册、路由、权限等等相关功能。这个中后台模板是基于我们实际开发定制的,所以跟通用的中后台模板(vue-element-admin)还不一样,可以认为是快速搭建系统的一种解决方案。

问题

在搭建了多个系统之后,我们遇到了点问题。项目模板在前期并不是特定稳定,有些功能需要进行调整,如果我们需要调整则需要在所有项目都进行手动调整,这会变得非常麻烦。随着系统的不断增加,这种每个系统都需要手动调整的方案势必会造成大量的资源浪费。所以我们开始讨论如何解决这个问题。

系统架构

在提出正式解决方案之前,我先简单介绍一下我们系统架构思路。我们前后端项目都是基于若依系统进行改造的,后端是使用Spirng cloud Gateway 将 api 分发到各个项目的微服务中,前端则是通过设置请求头来告知当前系统访问的项目。
应用部署架构

方案一 – Monorepo

Monorepo – 单体仓库基建方案。将多个开发项目放到一个项目中进行管理的一种手段。这种方案的优势和劣势也是显而易见的:

优势

  • 代码重用将变得非常容易:由于所有的项目代码都集中于一个代码仓库,我们将很容易抽离出各个项目共用的业务组件或工具,并通过 TypeScript,Lerna 或其他工具进行代码内引用;
  • 依赖管理将变得非常简单:同理,由于项目之间的引用路径内化在同一个仓库之中,我们很容易追踪当某个项目的代码修改后,会影响到其他哪些项目。通过使用一些工具,我们将很容易地做到版本依赖管理和版本号自动升级;
  • 代码重构将变得非常便捷:想想究竟是什么在阻止您进行代码重构,很多时候,原因来自于「不确定性」,您不确定对某个项目的修改是否对于其他项目而言是「致命的」,出于对未知的恐惧,您会倾向于不重构代码,这将导致整个项目代码的腐烂度会以惊人的速度增长。而在 monorepo 策略的指导下,您能够明确知道您的代码的影响范围,并且能够对被影响的项目可以进行统一的测试,这会鼓励您不断优化代码;
  • 它倡导了一种开放,透明,共享的组织文化,这有利于开发者成长,代码质量的提升:在 monorepo 策略下,每个开发者都被鼓励去查看,修改他人的代码(只要有必要),同时,也会激起开发者维护代码,和编写单元测试的责任心(毕竟朋友来访之前,我们从不介意自己的房子究竟有多乱),这将会形成一种良性的技术氛围,从而保障整个组织的代码质量。

劣势

  • 项目粒度的权限管理变得非常复杂:无论是 Git 还是其他 VCS 系统,在支持 monorepo 策略中项目粒度的权限管理上都没有令人满意的方案,这意味着 A 部门的 a 项目若是不想被 B 部门的开发者看到就很难了。(好在我们可以将 monorepo 策略实践在「项目级」这个层次上,这才是我们这篇文章的主题,我们后面会再次明确它);
  • 新员工的学习成本变高:不同于一个项目一个代码仓库这种模式下,组织新人只要熟悉特定代码仓库下的代码逻辑,在 monorepo 策略下,新人可能不得不花更多精力来理清各个代码仓库之间的相互逻辑,当然这个成本可以通过新人文档的方式来解决,但维护文档的新鲜又需要消耗额外的人力;
  • 对于公司级别的 monorepo 策略而言,需要专门的 VFS 系统,自动重构工具的支持:设想一下 Google 这样的企业是如何将十亿行的代码存储在一个仓库之中的?开发人员每次拉取代码需要等待多久?各个项目代码之间又如何实现权限管理,敏捷发布?任何简单的策略乘以足够的规模量级都会产生一个奇迹(不管是好是坏),对于中小企业而言,如果没有像 Google,Facebook 这样雄厚的人力资源,把所有项目代码放在同一个仓库里这个美好的愿望就只能是个空中楼阁。

从上述引用来看,monorepo的劣势对于我们小团队太过于沉重了。第一,我们没有足够的人力资源,这个问题还不单单在于项目权限的控制,更在与我们团队的人员水平和精力 – 是的,不得不承认团队水平参差不齐,并非所有人都愿意去付诸精力和汗水去学习。第二,我们已有的需求并不一定适合这种解决方式。我们的目的是快速搭建各个系统,前端并不需要微服务化,各个仓库之间没有明确调用关系。第三,对于现有系统的改造会很困难。我们每个系统都要重复的功能页面,但是我们并不能保证所有的前端页面都是一样的表现形式 – 可能针对某个特殊项目有功能调整。基于这一点我们如果要使用Monorepo的进行代码调整的话,我们需要将基础框架与页面、组件进行分离。这个工作量相当庞大,我们希望有更加轻量级的解决方案。

方案二 – 插件开发模式

插件开发模式 – 将系统分成shell和runtime模块,shell视为宿主,runtime视为插件。每个项目都包含shell和runtime两个部分,其中shell是包含基础的功能,包含开发、打包和基础的框架内容,runtime包含了业务相关模块。shell通过读取每个项目的配置文件进行调整,shell运行时会加载runtime的相关业务模块。

优势

这种开发方式好处是可以将 基础框架 与 业务开发 进行分离,如果后续我们需要升级shell 时就会变得相对简单(如果业务本身不需要修改shell的话),同时分离还降低了系统开发的复杂程度,使常规开发人员只关注业务本身的开发就好了。

劣势

  • 完全剥离框架与业务困难:目前的项目模板就是根据业务系统定制的,系统模板本身就集成了部分业务模块。但是我们并不能保证已经集成的业务模块不会发生变动或者需要新增一些新的公共业务模块。剥离基础框架和业务并不困难,困难的是我们的框架本身包含了公共的业务模块,我们无法对这块儿进行较好的处理。
  • 短期工作量大,难以渐进完成:框架杂糅了业务模块,如果需要采取插件模式开发,对现有的调整框架的调整较大。 一是要划分清楚框架与业务代码,对shell和runtime进行分离;二是要编写配置模块,明确需要读取哪些配置文件;三是已有的每个系统都需要按照这种较大的改动的方式去做,工作量大。

这确实是一种思路,但是我认为它适用的场景是那种 shell 与 runtime 分离比较容易的情况,类似于APP或者是小程序开发 – 有一个统一的shell运行各个需要加载的模块,降低不同开发人员使用不同的shell导致多个项目合并打包失败的问题。

方案三

物料市场。

物料即组成一个前端项目的不同单位,根据抽象粒度的不同,我们将物料从小到大分为 组件(component)、区块(block)和模板(template) 。在基于物料体系的开发中,我们使用模板物料来初始化前端工程,提供最佳实践,解决工程问题,再使用区块和组件像搭积木一样快速搭建页面。
物料分为 组件(component)区块(block)模板(template) 三种类型:

  • 组件(component):组件是组成页面的基本结构单元,是对局部交互逻辑的抽象和封装。通常需要设计和暴露属性、插槽、事件和方法等 API。使用者根据这些 API 直接使用,一般不需要做二次修改。
    从业务维度去看,组件又可分为基础组件和业务组件两种:
    • 基础组件:与业务无关的组件,基础组件保持统一的视觉规范,考虑高内聚低耦合的设计思想,例如 UButton、UInput、UTableView 等,在 Vusion 体系,官方维护 Cloud UI 基础组件库;
    • 业务组件:面向业务的组件,一般功能比较确定可复用,同时复杂度较高,例如用户选择器、计费卡片等。
  • 区块(block):区块可以理解为在页面中,由一些组件组合而成的代码片段。在一个页面中,使用者可以快速把某个区块的代码添加到自身项目里,进行二次修改。
  • 模板(template):项目的样板工程,包含了完整前端项目所有组成部分,包括布局、常用页面、基础插件、工程配置等,用户可以快速初始化项目。

我们已有中后台框架模板,就是对应到了物料中的模板这块儿。对于我个人而言我更加倾向于采取这种策略,原因如下:

  • 改造中后台框架调整工作量可控的。根据我们对物料的抽象程度不同,我们可以对项目进行不颗粒度划分。举例说明,我们将登录页面及其相关功能做成区块,那么新的项目就可以直接添加到进去,并根据业务要求进行修改。如果是需要调整,在将区块下载下来后,能做到快速更新的能力。

其他相关优点:

  • 最大化资源复用。项目,团队,成员之间轻松共享。
  • 提升迭代上下游协作效率。
  • 提升人效比。提升项目中前端开发人员的人效比,让前端做的更快、更多、更好;
  • 能力中台化。支撑业务快速发展。

参考链接:
All in one:项目级 monorepo 策略最佳实践
什么是物料
物料前端中台建设
从生产到消费,设计基于物料的前端开发链路
从业务组件库看前端物料生态
如何建设前端物料平台?

http://www.lryc.cn/news/7773.html

相关文章:

  • GEE学习笔记八十八:在自己的APP中使用绘制矢量(上)
  • “笨办法”学Python 3 ——练习 39. 字典,可爱的字典
  • 模糊的照片如何修复清晰?
  • 如何理解​session、cookie、token的区别与联系?
  • 【MyBatis】| MyBatis分页插件PageHelper
  • Java枚举类详解
  • C语言数组
  • Scala 入门(第一章Scala 环境搭建、插件的安装)
  • math@多项式@求和式乘法@代数学基本定理
  • Kafka系列之:基于SCRAM和Ranger机制完成动态新增kafka读写账号、赋予账号对指定Topic的读写权限
  • 第五十三章 DFS进阶(一)——剪枝优化
  • Java字节码深度知多少?
  • 【C++】智能指针(万字详解)
  • 使用docker配置mysql主从复制
  • v3 异步组件及分包使用
  • 实用调试技巧【上篇】
  • JavaScript 教程
  • 在SpringBoot里面使用原生的Servlet
  • 商标被驳回,先别慌!挽回商标有办法
  • VMware安装Linux虚拟机后忘记root密码处理方法
  • Centos安装OpenResty
  • 阿里云部署SpringBoot项目
  • EdgeCOM嵌入式边缘计算机的参数配置
  • 字节软件测试岗:惨不忍睹的三面,幸好做足了准备,月薪15k,拿到offer
  • 【编程基础之Python】5、安装Python第三方模块
  • JavaScript 教程导读
  • BigDecimal
  • 代码随想录【Day15】|102. 二叉树的层序遍历、226. 翻转二叉树、101. 对称二叉树
  • Python学习笔记:快速上手:基础知识
  • excel学习笔记-导入外部文件,报错,数值格式变换,日期格式的转化,求和快捷键,冻结窗格