背景

公司目前基于多业务部门,很多业务组件和功能逻辑都具有较高的普适性,但与此同时各业务部分和开发人员缺乏一定的交流平台,更多的是在遇到对应需求时会简单内部讨论一番,当了解到其他业务部分存在落地的方案时,再进一步进行沟通交流。

这种方式,总体来说还是比较原始的,无论从沟通方式的效率来说,还是对于组件业务逻辑的深入理解都是非常低效的。因此,对于探索一种高效的、简单的、现代化方案是非常有必要的。

我们想要的结果是(任务目标):

  1. 不同的业务平台开发的组件能够最大程度复用;
  2. 新人能够快速了解通用组件,提升开发效率

这也就是本系列文章的真实背景,也是我着手负责的项目,相关文章会按照系列形式输出,并确保文章内容都是在真实业务中的实践总结。

简要说明

作为本系列的第一篇文章,其定位和作用也是相当清晰的:

  1. 搞清楚前端组件化是什么?
  2. 搞明白组件化要怎么做?
  3. 思考如何把组件化做的更好?

前期主要以第1、2两点作为执行方向进行探索,在基本明白了什么是组件化以及如何实现自己的组件化之后,进一步探索、对比、研究、思考如何实现高效能的组件化方案。

前端的发展路径

在互联网早期阶段,传统的开发模式中,往往是把前端的网页代码和后端的程序代码混合在一起,借助某种模板技术(如 JSPASPPHP)来在服务器端动态生成 HTML 页面。

在这种开发模式下,网页的每次改动都需要前后端人员共同参与才能完成,网站前后端的开发人员需要很大的沟通成本、协调成本。企业招人的时候,也不得不招一些既懂前端又懂后端程序员,来减少前后端开发人员的冲突。

可以发现这种模式是非常低效的。

现在流行的前后端分离的开发模式,就是让后端只负责给前端提供数据,由前端负责整个页面的模板渲染、数据填充以及交互逻辑。

前后端分离之后,人们发现前端已经不再是传统意义上的网页了,它甚至还可以做成一个手机应用,或者做成是微信小程序那样的小型应用,它更接近于传统的 B/S(客户端/服务器)架构,且仍然具备网页轻量级、无需下载和安装的优势

什么是组件化

在前后端分离的现代开发模式下,以ReactVue为例,涌现了很多优秀的现代化的前端框架,得益于新技术的发展,SPA 应用已经相当成熟,该开发范式也是相当普遍。

伴随着这种技术行为,组件化开发也是应运而生,作为一个非常常规的模式,围绕几点说明:

  1. 方便复用(很多业务代码、功能代码都不可避免有所重复,对于组件的复用使用,可谓作用很大)
  2. 方便维护(如果大量的业务代码与功能代码耦合一起,对于代码的日后维护和功能拓展都有着很大的局限性和不足)
  3. 功能细分、专一、职责明确(组件的核心原则就是,一个组件只做一件事,而且其不应该是有上下强关联耦合性的,可以随用随取,做到职责单一,对于日后功能迭代和代码维护好处都是显而易见的)

就现代前端而言,我觉得组件化已不单单局限于 UI 组件层面,它涉及面更广、更全、也更系统化,类似 hooks 的功能和业务逻辑封装,工具类的使用封装,构建工具、脚本代码等,一切符合其独立思想的都可称为组件化。

为什么需要组件化

组件化的目的:

  1. 为了让各功能和业务逻辑可以被复用,以减少重复的代码。
  2. 可以更好地使团队分工协作,不同的人负责编写不同的组件,提高开发效率。
  3. 职责单一也更方便功能的迭代拓展与维护,对于接手的开发人员也能降低不少上手成本

对于需要开发复杂的大型应用的企业来说,组件化开发能极大地提高开发效率,它让前端开发团队能高效地完成工作,是一个非常有用的技术。

简单来说就是两个关键词:“复用”、“易维护

代码拆分也好,职责单一也好,基本都离不开这两词,这也正式组件化的核心点。我们做这些事,就是为了之后遇到类似需求和功能的时候减少代码的开发,能够快速重复使用。而对于组件在日后随着功能和业务的发展、包括我们对于组件代码健壮性的提升,都会进行不断的迭代,维护性的重要性也是不言而喻的了。

基本目标

围绕核心述求可以进一步细化梳理,我们希望它能够这样:

  1. 可以围绕基础组件、业务组件,以及工具类的定位进行划分
  2. 项目方案要足够简单清晰,确保新人上手成本足够低,可以快速投入开发使用
  3. 组件的可维护性需要有保障
  4. 严格的规范和工程化保障
  5. 支持查看API、示例的文档网站
  6. 真实开发环境的模拟调试和开发
  7. 能够以npm包的形式进行引用使用
  8. 可靠的多包项目管理、多包版本管理
  9. 足够优秀的性能表现

技术背景

首先,以公司实际项目为背景思考,我们的技术栈目前主要围绕 vue2 相关生态进行前端 web 后台类系统的开发。因此,主要方案设计也要以此为基础导向进行探索。

就目前而言,我们的组件主要包括可复性较高的业务组件以及一些通用功能组件,也包括一些工具类。组件开发主要依赖于iview基础组件和lodash工具库,部分特殊业务有使用到monaco-editorcodemirror等。

因此,现阶段我们公司的技术栈相关生态主要为 vue2,本系列也会以公司的vue2项目经历为依托进行记录。不过结合当下前端整体快速发展的现实情况下考虑,在项目实施落地之后,对相关生态具有足够清晰地掌握之后,以vue3react为技术背景的探索也值得进一步探索研究。

包依赖

我们公司当前项目平台主要依赖分析,如下:

  • 核心三大件:vue/vue-router/vuex
  • UI 类:view-designcodemirrormonaco-editorvue-echartsvuedraggble
  • 工具类:lodashdayjsuuid

围绕以上技术相关生态,做到对各部分及功能使用的二次封装管理

主要 API 及实际项目代码分析:

  • slot:组件拆分、UI 布局、自定义展示区域的好方法
  • props:父组件向子组件传递参数
  • ref:父组件获取子组件方法、属性
  • emit:子组件向父组件传递参数,触发父组件绑定事件方法
  • provide/inject:多级子父组件传值
  • vuex:第三方状态管理(需要考虑单一组件的可维护性)

总的来说组件封装较频繁使用的属性方法为props/slot/emit/ref,也是我们项目中大量采用的方式。

vuex 的使用需要谨慎考虑,具有一定的耦合性,需要进一步探索分析。

对于更优的方式探索值得思考,核心点为职责单一独立、耦合性低,方便维护。

功能/业务组件

业务组件不同于功能组件,其具有一定的使用背景和条件限制。以我们使用的 iviewantd等UI组件为例,其提供了大量的功能组件,包括但不限于表单功能组件Table列表功能组件多选/单选功能组件日期功能组件等等,它只提供某一类的通用功能,并抽象封装为一个具体的功能组件。

而相对于业务组件来说就有些许不同了,业务组件首先必定聚焦于一个明确的业务场景,其最终实现可能是融合了多个功能组件的组合,也可能是结合一些插件独立实现的某一特殊功能。

当前项目已存在的一些业务组件分析:

  • ProTable功能组件:由单个FilterBtn组件Table组件Pagination组件,进而组合成整体的一个组件(包含列表、筛选、分页)
  • ComSqlTipTree业务组件:使用频率相当高的 ComSqlTipTree组件,也是集合了众多子组件的一个组合(涉及到对Input的高级封装、Select选择、拼音匹配、列表展示等,该组件专门用于公司核心功能筛选类查找时的使用)

组件管理思考

  1. 基础组件类:可以参照iviewantd等UI组件库的方式进行维护管理,聚焦于功能相关,属于较小型的组件,统一维护管理。
  2. 业务组件类:可以参照pro-component高级组件的管理模式进行处理。
  3. 工具类:可以参照lodash的方式进行维护管理

pro-component的管理模式为大量基于基础组件封装而成的高级组件,如:pro-layoutpro-tablepro-form等,每个组件可以作为独立的npm包作为依赖使用,相互独立、互不影响。

再思考到对于工具类的管理维护,整体考虑下来,基本确定可以为多包管理的方向(Monorepo

技术调研

围绕项目技术背景和目标规划,进行相关技术调研。

相关开源项目分析

  1. 需要考虑的方向是:对于 vue2.x 版本的组件化方案,哪种更为合适(目前市面上有大量的vue3组件库)
  2. vue2 对于 TS 的考虑 (vue2对ts的支持性一般,是否支持视情况而定)
  3. props 参数 API 的文档、demo演示,文档网站建设的考虑
  4. 目录设计、工程规范、构建发布流程
  • element
    • monorepo,属于比较传统的单一包管理项目
    • 比价经典的vue2组件库项目,文档网站风格很不错
    • 主要使用 js + vue开发,构建由webpack处理
    • 文档网站由自身组件开发搭建,结合webpack loader实现对markdown文件的编译及demo示例的渲染
  • vuetify
    • lernajs + yarn + workspace 的方案,标准的 lernajs + yarn 用法
    • vue2.x + ts 写法,组件大量以 JSX 实现,具有很好的参考学习价值(vue2 中对 TS 的使用)
    • 文档网站由自身组件自行开发搭建,项目代码较多,有一定的上手成本
  • element-plus
    • vue3 的项目,pnpm + workspace 的方案,整体思路和设计非常现代化
    • 组件实现由 vue编写开发,相关脚本代码由 TS编写,整个项目为 TS项目
    • 多包目录设计、组件和文档目录设计合理
    • 相关构建工具包括 rollupesbuildvite
    • 文档网站由vitepress构建,并自行编写了处理markdownvue demo的插件进行处理(vitepress只支持vue3
  • idux
    • vue3 的项目,pnpm + workspace 的方案,技术都比较新
    • 版本控制由 lerna管理
    • demodocs的目录管理很不错,需要配合脚本实现最终的网站路由和渲染
    • 文档网站由 vitepress + 自身组件构建,并配合各种脚本实现对应功能
    • 构建工具主要为 gulp
    • 组件开发主要为 tsx形式

monorepo 是什么

  • Monorepo:所有依赖库完全放入一个项目工程
  • Multirepo:多个依赖包独立进行 git 管理

monorepo 是把多个项目的所有代码放到一个 git 仓库中进行管理,多个项目中会有共享的代码则可以分包引用。整个项目就是有 root 管理的 dependencies 加上多个 packages,每个 package 也可以在自己的作用域引入自己的 dependencies

该方案可以很好的帮助我们解决不同工作区对于相同依赖的管理,以及我们在开发时,包相互间依赖的管理。

monorepo + lernajs + yarn

Lerna 是一种工具,针对使用 gitnpm 管理多软件包代码仓库的工作流程进行优化。

Lerna 有两种管理项目的模式:

  • Fixed/Locked 模式 (默认): 所有的包共用一个版本号。
  • Independent mode:不同包独立使用自己的版本,我们一般采用这种方式。在初始化的时候指定 --independent 参数

一般而言,在pnpm未出现前,相关workspace概念基本是配合 yarn一起使用(yarnworkspace功能),因为 npm7.x之前是没有 workspace概念的,所以在很长的一段时间里,多包管理方案 monorepo都是与 yarn一起配合使用。

他解决了以下问题:

  • 多业务组件、互相依赖、无法复用
  • 发包流程复杂、版本管理痛苦

依赖管理

在工作区 packages 下面我们有这些包:iviewpro-sqltiptreecomponents,其 package.json 中的 name 定义分别为@ah-ailpha/iview@ah-ailpha/pro-sqltiptre@ah-ailpha/components,在我们相互安装包时,lerna 工具可以帮我们以用软链接的形式直接使用。(对于软链接的概念可以参考 npm link,以及pnpm的底层实现逻辑)

# 向 module-2 中添加 module-1 作为依赖
$ lerna add module-1 --scope=module-2
复制代码

版本管理

对于monorepo的多包场景,lerna另一大核心功能就是可以很好的帮助我们管理每个包的版本,通过使用 lerna version命令即可(可结合命令式交互快速管理版本)

lerna version 在背后为我们做了这些事:

  • 识别出自上次发布以后更新过的包;
  • 提示选择新版本;
  • 修改包的元数据来反映最新发版(修改包的版本号),在根目录和每个包里面运行生命周期脚本;
  • 对提交打 tag;
  • 推送到远程代码仓库。

monorepo + pnpm

pnpm现在也支持workspace的概念了工作空间(Workspace),如果是经常关注技术社区论坛的,相信你对pnpm已经有过了解了,pnpm因为其优秀的性能表现,以及其对monorepo的支持也更简单方便,使得现在很多知名的开源项目都采用了该方案,如 Vue3/Vite/VueUse/Element Plus/Next.js/Astro等等。

pnpm 内置了对单一存储库(也称为多包存储库、多项目存储库或单体存储库)的支持, 你可以创建一个 workspace 以将多个项目合并到一个仓库中。

一个 workspace 的根目录下必须有 pnpm-workspace.yaml 文件, 也可能会有 .npmrc 文件。

如何高效的使用 pnpm,需要了解几个常用的基本命令:

  • --filter:过滤,过滤允许您将命令限制于包的特定子集,一般用于 packages/* 下面的子项目。
  • -C:在 <path> 中启动 pnpm ,而不是当前的工作目录。
  • -r:安装在所有 packages 中(一般配合--filter指定项目目录)
  • -w:表示把包安装在 root 下,该包会放置在 <root>/node_modules

pnpm同样可以实现对包依赖的管理,类似lerna一样,通过软连接的方式关联到子项目,可以极大的方便我们对于多项目依赖的管理。

pnpm add <package-name> --filter <target-package-name>

# 比如要将lodash装到package-a下
# --filter 后面可以为目录名称也可以为 package.josn 的 name 名称
pnpm add dayjs --filter @ah-ailpha/package-a

# 向 module-2 中添加 module-1 作为依赖
pnpm add module-1 --filter module-2
复制代码

lerna / changeset 版本管理分析

不管对于 yarn还是 pnpm来说,都只是对于工作空间 woekspace的多包管理,对于多依赖管理,pnpm目前可以支持,而yarn是需要lerna进行协助的。

但是对于各包的version版本控制,两者都不直接参与管理,只能使用第三方工具进行处理。

lerna

目前已知的比较成熟的方案有lerna,也是使用比较频繁的,好处是其功能比较全面,但也存在一些明显的问题

  1. 不能和pnpm很好的适配,lerna 本身不支持 workspace 协议,导致基于 pnpm 开发的一些仓库无法使用
  2. lerna version 根据 commit 以及 tag 更新出来的包版本不符合预期
  3. 整个包的体积很大,依赖特别多(轻量版lerna-lite
  4. 相比较pnpm而言,lerna使用起来还是比较的笨重、繁琐

changeset

Changesets 是一个用于 Monorepo 项目下版本以及 Changelog 文件管理的工具(目前一些比较火的 Monorepo 仓库都在使用该工具进行项目的发包例如 pnpmmobx 等)

changesets 主要关心 monorepo 项目下子项目版本的更新changelog 文件生成包的发布

一个 changeset 是个包含了在某个分支或者 commit 上改动信息的 md 文件,它会包含这样一些信息:

  • 需要发布的包
  • 包版本的更新层级(遵循 semver 规范)
  • CHANGELOG 信息

总的来说,changeset的工作内容还是比较明确的,我们需要直接参与的就是包版本的管理与发布,对于changelog的文件生成会由脚本自动处理,我们无需关心(需要按照commitlint规范提交message,这里到时我们通过工程化的方式进行控制即可)

版本控制管理小结

  1. 使用 yarn + monorepo的项目一般使用lerna配合进行依赖和版本管理
  2. 采用 pnpm + workspace方案的 monorepo,可以选择使用 changeset进行版本管理控制,从而快速搭建一个完整的 monorepo项目。

尾声

本篇文章算得上是搭建本项目的一些前期探究,在从0到1的过程还是比较迷茫的,使用什么方案,哪种方案适合我,在找到适合的方案时又发现并不是我喜欢的技术方向时,如何做出选择,以及如何选择最优解等等,这些都是在我在实践的过程中所经历的。

总的来说,我比较关注技术社区和开源项目,一般出现了什么新的技术框架时,我也能及时的了解和尝试。在针对如何使用的这个问题上,通过结合官方文档和开源社区各种demo模板,也能略知一二,再简单上手使用对比下,大致能够有所了解,就我个人的项目而言我还是满喜欢尝鲜的,觉得好用、不错就会直接开整,遇到问题就尝试解决,也算是在这个过程中学习了。不过,对于公司的项目来说,总体偏稳定版、技术成熟度高、社区活跃的进行选择,在遇到问题时也能发现类似的解决方案。

monorepo算是我很早之前就打算研究和尝试的方向了,苦于一直没有合适的机会(😶懒~),在没有项目的推动下,自己也缺乏一定的动力,恰好现在公司在推进这个事情,而且还是由我进行负责,也算得上是个不错的机会了。

在开始之前我自己之前对于lerna是有一点了解的,因此,最开始在进行技术调研的时候也是大致围绕这个方向进行的,期间也看了不少开源项目的设计和使用方式,基本上算是入门了,不过,在我自己进行demo测试以及简单使用后,发现lerna的确有很多不太方便、不易用的地方,尤其是对于多项目命令的支持,远不及pnpm来的方便。

pnpm对于我来说并不算陌生,我在去年就开始在用了,整体还是比较顺手的,也比较喜欢,只是对于pnpm + workspace + monorepo这种方式没有实践过,不过在此之前倒是了解过不少相关的文章,印象较深的是介绍 vue生态的多包管理过渡到pnpm + workspace的文章,自己正好也一直想找机会尝试来着。

因此,在使用lerna发现一些较大的短板之后,我也是顺势过渡为pnpm + workspace的方案来了,以及在后期尝试了对于版本管理控制的changeset方案之后,最终确定的技术路线为pnpm + workspace + changesetmonorepo多包管理方案

实践篇相关文章见下一篇,欢迎关注(已经在写了😅~)。

资料

posted on 2022-12-09 15:35  下一个十年  阅读(219)  评论(0编辑  收藏  举报