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

前端Monorepo实践分享

在前端开发中,Monorepo(单一仓库)实践逐渐成为了一种趋势,许多大型科技公司如Google和Meta等都在实践中采用了Monorepo模式,并取得了显著的效果。例如,Google内部自研了Piper,能够支持基于文件级别的访问控制列表,对代码进行了更加精细化的管理。鉴于Monorepo实践在大型科技公司中的成功应用,以及它所带来的诸多优势,相信在未来,随着前端技术的持续进步和项目规模的不断扩大,Monorepo实践极有可能成为前端开发领域的主流模式之一。

 01 项目背景

原供应链前端工程代码采用多个代码仓库进行多工程的代码管理,并独立构建打包,随着业务发展,此方式暴露出如下现象:

  • 无法共同维护相同业务组件库和工具库;

  • 代码检测规范无法共享;

  • 项目开发构建包管理分散,因重复安装依赖,导致调试和构建打包耗时长;

  • 同类业务需求,需手动在多个工程代码进行复制同步,影响开发效率。

为了实现业务组件、配置和样式在不同工程间的共享,项目组前端团队引入了Monorepo技术。在Monorepo模式中,将业务组件置于共享目录下,便可通过源码引入实现代码共享,统一构建流程和代码规范,享受业务组件复用模式带来的开发便利,提高开发效率和代码维护质量。下表列出了Monorepo方式和多仓库方式的优缺点比较:

Monorepo Auto业务组件库基于中心统一BeP组件库进行开发实现,集成Auto组件的框架,可以保证UI交互一致性的同时,实现使用JSON配置来生成页面,从而减少页面开发工作量,极大提升效率。

02 工程架构

项目组采用"PNPM+Workspace"方案来实现Monorepo模式下跨应用工程架构。此工程架构的优势体现在:

  • 开发学习成本低;

  • 已有工程改造成本低;

  • 业务组件引用简单。

1 构建共享框架

1.1 Monorepo工程架构

Monorepo工程架构如下:

工程目录设计如下:

1.2 Monorepo初始化子项目方式

Monorepo主工程初始化子项目,可以采用下面两种方式中的一种:

方式1:合并代码库。多工程代码仓库子项目代码同步到主工程目录下面即可。

方式2:代码库分离。如果是已多工程代码仓库方式,可以通过git submodule命令进行子项目添加,这样可以达到再业务并行迭代的时候进行渐进式升级。

1.3 Monorepo主工程配置

Monorepo主工程配置安装全局依赖和对应子工程启动命令,使用pnpm安装依赖:

pnpm add vue@2.7.14 vuex@3.6.2 vue-router@3.6.5 pp-bgyfw-ui@1.0.31 -wD

在package.json中配置好对应子工程启动命令:

{
  "name":
 "scm-mono-apps",
  "version": "1.0.0",
  "scripts": {
    "op:dev": "pnpm --filter @scm-web/operation run serve",
  }
}

启用对应子工程过程及效果:

2 构建共享业务组件库

通过构建共享业务组件库,实现了跨多个项目的复用性。在本项目实践中,项目组开发了包括AutoForm、AutoInfo、AutoModel、AutoTab、AutoTable、AutoOperation等组件,形成了集中管理和共享一套公共的UI组件库。这些组件通过统一的样式和行为规范,显著增强了不同应用端用户体验的一致性。

2.1 Monorepo共享组件框架

Monorepo共享组件架构如下:

组件库目录设计如下:

2.2 业务组件库工程共享实现

在主仓的packages/components/package.json文件配置如下:

{
  "name": "@scm-web/components", // 提供给子项目工程安装使用
  "version": "1.0.0",
  "main": "index.js", // 在这里引入index.js文件
  ...
}

项目组在packages/components/index.js设立统一入口点,用于集中导出所有公共业务组件。为了高效管理和使用这些组件,项目组在Monorepo根目录中使用pnpm安装@scm-web/components包,然后在项目中引入所需的业务组件,即可快速集成和使用这些高质量的UI组件。

// packages/index.js

export { default as AutoForm } from './AutoForm/index.vue';
export { default as AutoTable } from './AutoTable/index.vue';
....

3 构建共享配置及规范

项目组通过引入Monorepo工程框架的工具链,实现了统一工程配置、统一代码检测规则及统一代码规范约束。同时通过采用原子化CSS引擎,增加开发样式编写效率,提升了整体代码的可读性、可维护性和可复用性。

以下是详细实现过程:

首先,主工程使用pnpm安装对应的开发业务工具链:eslint+prettier+husky+lint-staged+ commitlint+tailwindcss。各业务工具链工作原理见下图:

然后,配置相关业务工具链文件及工具目录设计:

最后,配置pre-commit命令实现自动提交检测和格式化。

构建共享工具类

通过在Monorepo中实现工具类的共享,多个应用端只需维护一套工具类代码,可达到工具一致性。Monorepo主仓根目录创建utils目录如下:

在主仓packages/utils/index.js入口文件中将工具类进行整合,并通过修改packages/utils/package.json文件,进行声明式共享业务工具包配置,确保各应用端能够统一引用和维护这套工具类。

export * from './lib/priceConversion';
export * from './lib/requestHook';
...

03 最佳实践

1 通用业务组件共享案例

在Monorepo仓库根目录执行相关命令安装组件,各子项目工程即可使用共享业务组件库。

// 供应链平台端工程添加业务组件库
pnpm add @scm-web/components --filter @scm-web/operation
// 供应链供应商工程添加业务组件库
pnpm add @scm-web/components --filter @scm-web/supplier

执行完pnpm添加命令后,再在对应的子工程/package.json中配置依赖。

通过JSON配置+@scm-web/components,生成交互页面流程见下图:

实现原理图解如下:

2 tailwindcss配置规范共享案例

首先,Monorepo主仓库目安装tailwindcss相关包依赖。接着,各子项目配置依赖父tailwind.config.js。然后,配置自定义原子化CSS index.css,在工程代码根目录main.js引入自定义样式

tailwindcss工作原理见下图:

通过定制的tailwindcss claa声明即可快速生成页面样式布局流程如下:

综上可见,在页面上使用原子化CSS后,摒弃了冗余繁琐的style。这种方式降低了学习成本低,提升了开发效率。

3 业务工具类共享案例

Monorepo仓库根目录执行相关命令安装utils包,并在相应子工程/package.json配置变更,对应的子仓即可识别到工作区的"@scm-web/utils"包。

各子项目可引入共享的utils包,参考如下引入:

import { createActions } from '@scm-web/utils';//使用utils包

export const usePurchaseStore = defineStore({
  id: 'order',  actions: {
    // 使用共享工具类方法实现工厂自动生成接口列表模式
    ...createActions(purchaseApi),
  },
});

04 项目效果

通过采用Monorepo相关技术栈升级工程架构,在供应链平台的项目上,解决了以下实际业务问题:

  1. 使用pnpm+workspace建立Monorepo框架,支持多项目间代码共享,提高开发效率和部署速度。

  2. 借助Monorepo的共享特性,封装了Auto组件库,实现多项目间业务组件共享,确保交互体验一致性和高效开发模式。

  3. 通过Monorepo的代码管理方式,实现了代码规范和配置的共享,提升了多工程维护效率和代码约束的一致性。

供应链平台端-主商品管理、供应商商品管理:

供应链供应商端-商品管理:


本文作者:

黄启聪:碧桂园服务前端开发高级工程师

杨玉梅:碧桂园服务前端开发高级工程师

伍诗聪:碧桂园服务前端开发主任工程师

胡天海:碧桂园服务产品研发高级工程师

指导人:

古松青:碧桂园服务技术总监

张旺其:碧桂园服务产品研发专家


http://www.kler.cn/a/461091.html

相关文章:

  • GXUOJ-算法-补题:22级《算法设计与分析》第一次课堂练习
  • LoRA微调系列笔记
  • 攻防靶场(29):目录权限和文件权限 ICMP
  • 3. C语言 数据类型
  • Word如何插入图片并移动到某个位置
  • 40.3 prometheus预聚合源码解读
  • GXUOJ-算法-第四次作业(圆排列、连续邮资、n皇后、符号三角形)
  • 「下载」智慧文旅运营综合平台解决方案:整体架构,核心功能设计
  • Rabbitmq追问2
  • UniApp 组件的深度运用
  • 【时时三省】(C语言基础)动态内存函数calloc
  • 活动安排.
  • nginx-nginx的缓存集成
  • Tube Qualify弯管测量系统在汽车管路三维检测中的应用
  • 08.VSCODE:内嵌MSYS2及三方库UTF8-CPP的实战
  • Spring 框架——@Async 注解
  • 可以突破合同相对性原则,不包括借用资质及多层转包和违法分包关系中的实际施工人
  • 【华为OD-E卷-统计匹配的二元组个数 100分(python、java、c++、js、c)】
  • 大语言模型遇上寻路算法:LLM-A*如何让路径规划效率提升50%?
  • 高效管理 Nginx 的利器:nginxWebUI 指南和 Docker 部署安装过程
  • 又一年。。。。。。
  • QT_BEGIN_NAMESPACE 和 QT_END_NAMESPACE (二)
  • 基于Spring Boot + Vue3实现的在线汽车保养维修预约管理系统源码+文档
  • MySQL 并行复制:提升数据同步速度的秘密武器
  • Maven (day04)
  • Type-C接口台式显示器:LDR6021引领新潮流