背景
公司目前基于多业务部门,很多业务组件和功能逻辑都具有较高的普适性,但与此同时各业务部分和开发人员缺乏一定的交流平台,更多的是在遇到对应需求时会简单内部讨论一番,当了解到其他业务部分存在落地的方案时,再进一步进行沟通交流。
这种方式,总体来说还是比较原始的,无论从沟通方式的效率来说,还是对于组件业务逻辑的深入理解都是非常低效的。因此,对于探索一种高效的、简单的、现代化方案是非常有必要的。
我们想要的结果是(任务目标):
- 不同的业务平台开发的组件能够最大程度复用;
- 新人能够快速了解通用组件,提升开发效率
这也就是本系列文章的真实背景,也是我着手负责的项目,相关文章会按照系列形式输出,并确保文章内容都是在真实业务中的实践总结。
简要说明
作为本系列的第一篇文章,其定位和作用也是相当清晰的:
- 搞清楚前端组件化是什么?
- 搞明白组件化要怎么做?
- 思考如何把组件化做的更好?
前期主要以第1、2两点作为执行方向进行探索,在基本明白了什么是组件化以及如何实现自己的组件化之后,进一步探索、对比、研究、思考如何实现高效能的组件化方案。
前端的发展路径
在互联网早期阶段,传统的开发模式中,往往是把前端的网页代码和后端的程序代码混合在一起,借助某种模板技术(如 JSP
、ASP
、PHP
)来在服务器端动态生成 HTML
页面。
在这种开发模式下,网页的每次改动都需要前后端人员共同参与才能完成,网站前后端的开发人员需要很大的沟通成本、协调成本。企业招人的时候,也不得不招一些既懂前端又懂后端程序员,来减少前后端开发人员的冲突。
可以发现这种模式是非常低效的。
现在流行的前后端分离的开发模式,就是让后端只负责给前端提供数据,由前端负责整个页面的模板渲染、数据填充以及交互逻辑。
前后端分离之后,人们发现前端已经不再是传统意义上的网页了,它甚至还可以做成一个手机应用,或者做成是微信小程序那样的小型应用,它更接近于传统的 B/S(客户端/服务器)架构,且仍然具备网页轻量级、无需下载和安装的优势。
什么是组件化
在前后端分离的现代开发模式下,以React
、Vue
为例,涌现了很多优秀的现代化的前端框架,得益于新技术的发展,SPA 应用已经相当成熟,该开发范式也是相当普遍。
伴随着这种技术行为,组件化开发也是应运而生,作为一个非常常规的模式,围绕几点说明:
- 方便复用(很多业务代码、功能代码都不可避免有所重复,对于组件的复用使用,可谓作用很大)
- 方便维护(如果大量的业务代码与功能代码耦合一起,对于代码的日后维护和功能拓展都有着很大的局限性和不足)
- 功能细分、专一、职责明确(组件的核心原则就是,一个组件只做一件事,而且其不应该是有上下强关联耦合性的,可以随用随取,做到职责单一,对于日后功能迭代和代码维护好处都是显而易见的)
就现代前端而言,我觉得组件化已不单单局限于 UI 组件层面,它涉及面更广、更全、也更系统化,类似 hooks
的功能和业务逻辑封装,工具类的使用封装,构建工具、脚本代码等,一切符合其独立思想的都可称为组件化。
为什么需要组件化
组件化的目的:
- 为了让各功能和业务逻辑可以被复用,以减少重复的代码。
- 可以更好地使团队分工协作,不同的人负责编写不同的组件,提高开发效率。
- 职责单一也更方便功能的迭代拓展与维护,对于接手的开发人员也能降低不少上手成本
对于需要开发复杂的大型应用的企业来说,组件化开发能极大地提高开发效率,它让前端开发团队能高效地完成工作,是一个非常有用的技术。
简单来说就是两个关键词:“复用”、“易维护”
代码拆分也好,职责单一也好,基本都离不开这两词,这也正式组件化的核心点。我们做这些事,就是为了之后遇到类似需求和功能的时候减少代码的开发,能够快速重复使用。而对于组件在日后随着功能和业务的发展、包括我们对于组件代码健壮性的提升,都会进行不断的迭代,维护性的重要性也是不言而喻的了。
基本目标
围绕核心述求可以进一步细化梳理,我们希望它能够这样:
- 可以围绕基础组件、业务组件,以及工具类的定位进行划分
- 项目方案要足够简单清晰,确保新人上手成本足够低,可以快速投入开发使用
- 组件的可维护性需要有保障
- 严格的规范和工程化保障
- 支持查看
API
、示例的文档网站 - 真实开发环境的模拟调试和开发
- 能够以
npm
包的形式进行引用使用 - 可靠的多包项目管理、多包版本管理
- 足够优秀的性能表现
技术背景
首先,以公司实际项目为背景思考,我们的技术栈目前主要围绕 vue2
相关生态进行前端 web 后台类系统的开发。因此,主要方案设计也要以此为基础导向进行探索。
就目前而言,我们的组件主要包括可复性较高的业务组件以及一些通用功能组件,也包括一些工具类。组件开发主要依赖于iview
基础组件和lodash
工具库,部分特殊业务有使用到monaco-editor
、codemirror
等。
因此,现阶段我们公司的技术栈相关生态主要为 vue2
,本系列也会以公司的vue2
项目经历为依托进行记录。不过结合当下前端整体快速发展的现实情况下考虑,在项目实施落地之后,对相关生态具有足够清晰地掌握之后,以vue3
和react
为技术背景的探索也值得进一步探索研究。
包依赖
我们公司当前项目平台主要依赖分析,如下:
- 核心三大件:
vue
/vue-router
/vuex
- UI 类:
view-design
、codemirror
、monaco-editor
、vue-echarts
、vuedraggble
- 工具类:
lodash
、dayjs
、uuid
围绕以上技术相关生态,做到对各部分及功能使用的二次封装管理
主要 API 及实际项目代码分析:
- slot:组件拆分、UI 布局、自定义展示区域的好方法
- props:父组件向子组件传递参数
- ref:父组件获取子组件方法、属性
- emit:子组件向父组件传递参数,触发父组件绑定事件方法
- provide/inject:多级子父组件传值
- vuex:第三方状态管理(需要考虑单一组件的可维护性)
总的来说组件封装较频繁使用的属性方法为props
/slot
/emit
/ref
,也是我们项目中大量采用的方式。
vuex
的使用需要谨慎考虑,具有一定的耦合性,需要进一步探索分析。
对于更优的方式探索值得思考,核心点为职责单一独立、耦合性低,方便维护。
功能/业务组件
业务组件不同于功能组件,其具有一定的使用背景和条件限制。以我们使用的 iview
、antd
等UI组件为例,其提供了大量的功能组件,包括但不限于表单功能组件
、Table列表功能组件
、多选/单选功能组件
、日期功能组件
等等,它只提供某一类的通用功能,并抽象封装为一个具体的功能组件。
而相对于业务组件来说就有些许不同了,业务组件首先必定聚焦于一个明确的业务场景,其最终实现可能是融合了多个功能组件的组合,也可能是结合一些插件独立实现的某一特殊功能。
当前项目已存在的一些业务组件分析:
- ProTable功能组件:由单个
FilterBtn组件
、Table组件
、Pagination组件
,进而组合成整体的一个组件(包含列表、筛选、分页) - ComSqlTipTree业务组件:使用频率相当高的
ComSqlTipTree组件
,也是集合了众多子组件的一个组合(涉及到对Input
的高级封装、Select
选择、拼音匹配、列表展示等,该组件专门用于公司核心功能筛选类查找时的使用)
组件管理思考
- 基础组件类:可以参照
iview
、antd
等UI组件库的方式进行维护管理,聚焦于功能相关,属于较小型的组件,统一维护管理。 - 业务组件类:可以参照
pro-component
高级组件的管理模式进行处理。 - 工具类:可以参照
lodash
的方式进行维护管理
pro-component
的管理模式为大量基于基础组件封装而成的高级组件,如:pro-layout
、pro-table
、pro-form
等,每个组件可以作为独立的npm
包作为依赖使用,相互独立、互不影响。
再思考到对于工具类的管理维护,整体考虑下来,基本确定可以为多包管理的方向(Monorepo)
技术调研
围绕项目技术背景和目标规划,进行相关技术调研。
相关开源项目分析
- 需要考虑的方向是:对于
vue2.x
版本的组件化方案,哪种更为合适(目前市面上有大量的vue3
组件库) vue2
对于TS
的考虑 (vue2对ts的支持性一般,是否支持视情况而定)- 对
props
参数API
的文档、demo演示,文档网站建设的考虑 - 目录设计、工程规范、构建发布流程
- 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项目
- 多包目录设计、组件和文档目录设计合理
- 相关构建工具包括
rollup
、esbuild
、vite
- 文档网站由
vitepress
构建,并自行编写了处理markdown
和vue demo
的插件进行处理(vitepress
只支持vue3
)
- idux
vue3
的项目,pnpm + workspace
的方案,技术都比较新- 版本控制由
lerna
管理 demo
与docs
的目录管理很不错,需要配合脚本实现最终的网站路由和渲染- 文档网站由
vitepress
+ 自身组件构建,并配合各种脚本实现对应功能 - 构建工具主要为
gulp
- 组件开发主要为
tsx
形式
monorepo 是什么
- Monorepo:所有依赖库完全放入一个项目工程
- Multirepo:多个依赖包独立进行 git 管理
monorepo
是把多个项目的所有代码放到一个 git
仓库中进行管理,多个项目中会有共享的代码则可以分包引用。整个项目就是有 root 管理的 dependencies
加上多个 packages
,每个 package
也可以在自己的作用域引入自己的 dependencies
。
该方案可以很好的帮助我们解决不同工作区对于相同依赖的管理,以及我们在开发时,包相互间依赖的管理。
monorepo + lernajs + yarn
Lerna
是一种工具,针对使用 git
和 npm
管理多软件包代码仓库的工作流程进行优化。
Lerna
有两种管理项目的模式:
Fixed/Locked
模式 (默认): 所有的包共用一个版本号。Independent mode
:不同包独立使用自己的版本,我们一般采用这种方式。在初始化的时候指定--independent
参数
一般而言,在pnpm
未出现前,相关workspace
概念基本是配合 yarn
一起使用(yarn
有workspace
功能),因为 npm7.x
之前是没有 workspace
概念的,所以在很长的一段时间里,多包管理方案 monorepo
都是与 yarn
一起配合使用。
他解决了以下问题:
- 多业务组件、互相依赖、无法复用
- 发包流程复杂、版本管理痛苦
依赖管理
在工作区 packages
下面我们有这些包:iview
、pro-sqltiptree
、components
,其 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
,也是使用比较频繁的,好处是其功能比较全面,但也存在一些明显的问题
- 不能和
pnpm
很好的适配,lerna
本身不支持workspace
协议,导致基于pnpm
开发的一些仓库无法使用 lerna version
根据commit
以及tag
更新出来的包版本不符合预期- 整个包的体积很大,依赖特别多(轻量版lerna-lite)
- 相比较
pnpm
而言,lerna
使用起来还是比较的笨重、繁琐
changeset
Changesets
是一个用于 Monorepo 项目下版本以及 Changelog
文件管理的工具(目前一些比较火的 Monorepo 仓库都在使用该工具进行项目的发包例如 pnpm
、mobx
等)
changesets
主要关心 monorepo
项目下子项目版本的更新、changelog 文件生成、包的发布。
一个 changeset
是个包含了在某个分支或者 commit
上改动信息的 md
文件,它会包含这样一些信息:
- 需要发布的包
- 包版本的更新层级(遵循
semver
规范) CHANGELOG
信息
总的来说,changeset
的工作内容还是比较明确的,我们需要直接参与的就是包版本的管理与发布,对于changelog
的文件生成会由脚本自动处理,我们无需关心(需要按照commitlint
规范提交message
,这里到时我们通过工程化的方式进行控制即可)
版本控制管理小结
- 使用
yarn + monorepo
的项目一般使用lerna配合进行依赖和版本管理 - 采用
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 + changeset
的monorepo多包管理方案。
实践篇相关文章见下一篇,欢迎关注(已经在写了😅~)。