使用Melos管理分散式的组件化
前言
在前文方案一【传送门】 中,我们实现了树形组件化,随之我们在实践中得到了一些痛点,针对痛点我们实现完善了分散式的方案二【传送门】。
虽然已经是可以用的状态了,但是还是有着一些痛点和不方便的地方。针对性的解决这些问题我们可以用到一些工具例如 Melos 来实现多包多组件的管理。
至于不同的方案他们的具体的思路和开发痛点,有兴趣的可以自行查看,如果对此没兴趣可以直接查看本文 Melos 相关的内容。
那么如何使用 Melos ,如何配合组件化使用,如何定义通用的脚本,这里我就抛砖引玉了。
一、如何安装使用Melos
其实很多人并不了解 Melos,它在 Fultter 开发领域并没有像 Pub 或者 前端的 NPM 那样必须,我们是可以用也可以不同,比我的电脑上安装了 Melos 使用 Mleos 的脚本编译和运行,但是我的同事他并没有安装 Melos 他也可以运行项目。根据依赖 Melos 的程度不同有不同的选择。
大多数情况下,我们创建一个 Flutter 项目,默认是一个单包项目,该项目由一个pubspec.yaml、lib文件夹组成。所有的逻辑和功能都在这一个包中实现,但是也有一些复杂的 Flutter 项目会按功能,模块,组件等拆分为多个包,如何方便的多包管理就需要用到 Melos 框架了。
Melos 是由Flutter社区的一个知名团队开发的,invertase。你可以在他们的网站上详细了解Melos。
它能实现的功能很多:
- 自动生成版本和更新日志。
- 自动发布软件包到pub.dev。
- 本地软件包的链接和安装。
- 同时执行各软件包的命令。
- 列出本地软件包和它们的依赖关系。
在我们这里的使用场景中,我们只用到本地的包管理和依赖关系等部分功能。
那么接下来我们首先安装 Mleos,这一点和安装 Fultter SDK 有点类似,也有所不同。我们需要通过 pub 全局安装,但是也需要配置环境变量。
所以首先要确定我们安装了 Flutter / Dart 才行。
接下来我们安装 Melos
dart pub global activate melos
我们运行试试:
不行哦,其实我们可以选择加入到环境变量中:
查看环境变量:
再次尝试:
那么我们的 Flutter 项目怎么引用这个全局的 Melos 呢?
现在在项目的跟目录创建一个名为 melos.yaml 的文件,并将以下内容复制到其中:
yaml
name: melos_demo
packages:
- apps/*
- package/*
command:
bootstrap:
usePubspecOverrides: true
scripts:
clean: melos exec -- flutter clean
pub-get: melos exec -- flutter pub get
pub-upgrade: melos exec -- flutter pub upgrade
analyze:
run: melos exec -- "flutter analyze"
description: Run `flutter analyze` in all packages
format:
run: melos exec -- "flutter format . --set-exit-if-changed"
description: Run `flutter format .` in all packages
在新版本中我们还需要添加一个默认的 pubspec.yaml 添加 dev 的 melos 插件
sql
dart pub add melos --dev
效果为:
yaml
name: demo
version: 1.0.0
description:
environment:
sdk: '>=3.0.2 <4.0.0'
dependencies:
flutter_lints: ^2.0.1
dev_dependencies:
melos: ^6.1.0
此时执行
melos bootstrap
会显示编译的模块以及成功的日志,此时就算是我们就已经基本的安装并配置完成了
二、如何定义子组件及作用域
以我们的 Flutter_Room 的项目示例,我们拆分了那么多组件,并且组件内部还能独立运行组件,我们怎么管理呢?
我们可以把每一个模块在 Melos 的配置文件中定义,后期不管是组件之间的依赖还是下面的脚本编写都可以更加的方便。
python
name: flutter_room
packages:
- "app/**"
- "packages/cs_domain/**"
- "packages/cs_initializer/**"
- "packages/cs_plugin_basic/**"
- "packages/cs_plugin_platform/**"
- "packages/cs_resources/**"
- "packages/cs_router/**"
- "packages/cs_shared/**"
- "packages/cs_widgets/**"
- "packages/cpt_auth/"
- "packages/cpt_mall/"
- "packages/cpt_profile/"
- "!packages/cpt_mall/runalone/**"
- "!packages/cpt_auth/runalone/**"
command:
bootstrap:
usePubspecOverrides: true
scripts:
analyze:
run: melos exec -- "flutter analyze"
description: Run `flutter analyze` in all packages
pub_get:
run: dart pub global run melos exec --flutter "flutter pub get"
description: pub get
build_all:
run: dart pub global run melos exec --depends-on="build_runner" "flutter packages pub run build_runner build --delete-conflicting-outputs"
description: build_runner build all modules
format:
run: melos exec -- "flutter format . --set-exit-if-changed"
description: Run `flutter format .` in all packages
test:
run: melos exec --dir-exists=test -- "flutter test"
description: Run `flutter test` in all packages
runalone_bootstrap_auth:
run: melos exec --dir="packages/cpt_auth/runalone" -- 'flutter pub get'
description: Bootstrap cpt_auth/runalone module
runalone_bootstrap_mall:
run: melos exec --dir="packages/cpt_mall/runalone" -- 'flutter pub get'
description: Bootstrap cpt_mall/runalone module
配置文件大致就是这样的,这也不算是一种语法,只是一种配置规则。我们依葫芦画瓢就能完成,仔细观察也就能发现他们的规律了。
在我们执行 melos bootstrap
之后就能自动生成对应的脚本
我们不管是运行宿主App 还是 独立组件都可以通过对应的脚本执行了,例如:
三、推荐脚本的写法以及常用脚本分享
在上面 melos 的配置文件中,模块的配置我们很容易理解,关于下面的脚本如何写如何定义我们可能有些疑问。
其实它们也并不神秘,也是有迹可循,例如:
perl
analyze:
run: melos exec -- "flutter analyze"
description: Run `flutter analyze` in all packages
最常见的 melos 命令 melos exec
是常用的功能,允许你一次性在所有子包中执行某个操作,而不需要手动进入每个子包目录逐个执行。
聪明的你一定想到了,我们组件化之后那么多组件每一个都要我们手动的去点击更新岂不是太麻烦,这里 Melos 就很方便:
arduino
pub_get:
run: melos exec --flutter "flutter pub get"
description: pub get
我们只需要自定义一个这样的脚本就能完事。
再次基础上那么还有一些其他的变种和限制条件,比如:
arduino
pub_get:
run: dart pub global run melos exec --flutter "flutter pub get"
description: pub get
其实它们是一样的功能,一个是依赖全局包,一个是当前路径包下运行,区别是你是否配置了 melos 的环境变量。所以我直接本地运行更合适。
除此之外我们还能限制区间内的更新,比如:
ini
melos exec --scope="cs_resources" -- "flutter pub get":
我们可以只执行资源模块的更新,可能有同学会说这有毛用?全部一起更新以免我一个个的点还有点用,这一个组件更新有毛用?
还真有用,有时候我们不需要更新全部的模块,比如我们实际开发中,一些组件是不会变动的,只有一些模块会随着我们的开发变化,比如资源和数据模块,所以我们一般只需要更新这两个模块。
我们就可以这样定义脚本:
ini
resource_get:
run: |
melos exec --scope="cs_resources" -- "flutter pub get"
melos exec --scope="cs_domain" -- "flutter pub get"
description: 更新实体和资源模块
使用 | 符号表示多行脚本执行,然后只���资源模块和数据模块执行。
我们的脚本只能执行 melos 内置的命令吗?其他的命令能执行吗?
当然也是可以的,我们以 Flutter 项目为例:
arduino
runalone_mall:
run: cd "$MELOS_ROOT_PATH/packages/cpt_mall/runalone" && flutter run
description: runalone mall module.
我们可以直接写命令,进入某一个目录执行某命令等非 melos 内置命令。
其效果就是运行独立模块了。
我们上面的一起运行几个模块的脚本,我们甚至还能"混编"
为我们特意的执行 clean all 脚本之后,再运行 source_get 脚本,这样就可以只更新部分组件,下面是效果:
我们可以看到只有这三个模块运行了 get 只有这三个模块没有报错。
为了特意演示两种不同的写法,我们使用不同的写法进行 pub get 操作都是一样的效果。
咦?新世界的大门打开了,那我们是不是可以进行其他的骚操作了?
是的!你甚至可以执行 windows 脚本,C ++ ,Python 等命令了,只要你的电脑环境支持。
例如我们鼎鼎大名的 flame 插件,它的 melos 就是这么玩的:
arduino
doc-kill:
run: cd "$MELOS_ROOT_PATH/doc/_sphinx" && python3 kill-server.py
description: Kills any TCP processes running on port 8000.
除此之外还有一些其他限制条件如 packageFilters 的用法,还有一些 --delete-conflicting-outputs 类似的运行配置,这些不常用的写法由于比较多比较杂就需要大家去探索啦。
后记
本文首先简单的介绍了Melos,以及 Mleos 的安装和集成,然后讲到 Melos 与组件化项目的各种配置,最后我们讲了一写 Melos 的配置写法以及一些常用配置分享。
本文的内容和代码和组件都是是基于方案二【传送门】实现的,如果你对此有疑问可以先看看。
除此之外,入门之后大家可以参考大型开源项目的 Melos 配置。
例如 Firebase 的 flutterfire,AWS 的 amplify-flutter, 游戏引擎 Flame,Flutter社区的 plus_plugins,字节的调试工具 flutter_ume 等。
这些大型项目都是使用 melos 进行多包管理,很多写法值得我们参考。
对于如何进行 melos 进行组件化的管理,也可以参考我的开源项目 【Flutter Room】。
那么今天的分享就到这里啦,当然如果你有其他的更多的更好的实现方式,也希望大家能评论区交流一起学习进步。如果我的文章有错别字,不通顺的,或者代码、注释、有错漏的地方,同学们都可以指出修正。
如果感觉本文对你有一点的启发和帮助,还望你能点赞
支持一下,你的支持对我真的很重要。
Ok,这一期就此完结。