FSA全栈行动 FSA全栈行动
首页
  • 移动端文章

    • Android
    • iOS
    • Flutter
  • 学习笔记

    • 《Kotlin快速入门进阶》笔记
    • 《Flutter从入门到实战》笔记
    • 《Flutter复习》笔记
前端
后端
  • 学习笔记

    • 《深入浅出设计模式Java版》笔记
  • 逆向
  • 分类
  • 标签
  • 归档
  • LinXunFeng
  • GitLqr

公众号:FSA全栈行动

记录学习过程中的知识
首页
  • 移动端文章

    • Android
    • iOS
    • Flutter
  • 学习笔记

    • 《Kotlin快速入门进阶》笔记
    • 《Flutter从入门到实战》笔记
    • 《Flutter复习》笔记
前端
后端
  • 学习笔记

    • 《深入浅出设计模式Java版》笔记
  • 逆向
  • 分类
  • 标签
  • 归档
  • LinXunFeng
  • GitLqr
  • AndroidUI

  • Android第三方SDK

  • Android混淆

  • Android仓库

  • Android新闻

  • Android系统开发

  • Android源码

  • Android注解AOP

  • Android脚本

  • AndroidTv开发

  • AndroidNDK

  • Android音视频

  • Android热修复

  • Android性能优化

  • Android云游戏

  • Android插件化

  • iOSUI

  • iOS工具

  • iOS底层原理与应用

  • iOS组件化

  • iOS音视频

  • iOS疑难杂症

  • iOS之Swift

  • iOS之RxSwift

  • iOS开源项目

  • iOS逆向

  • Flutter开发

    • Dart - 抽象类的实例化
    • Flutter - 打印好用的Debug日志
    • Flutter - 混合开发
    • Flutter - 解决混合开发iOS脚本打包遇到的问题
    • Flutter - 低版本在iOS14上遇到的问题与解决方案
    • Flutter - 解决原生弹窗的触摸事件被Flutter响应的问题
    • Flutter - 实现列表上下拉切换header
    • Flutter - 获取ListView当前正在显示的Widget信息
    • Flutter - 列表滚动定位超强辅助库,墙裂推荐!🔥
    • Flutter - 快速实现聊天会话列表的效果,完美💯
    • Flutter - 聊天输入框更新文本时的必备优化点🔖
    • Flutter - 我给官方提PR,解决run命令卡住问题 😃
    • Flutter - 探索run命令到底做了什么 🤔
    • Flutter - 引擎调试(iOS篇)🛠
    • Flutter - 引擎调试bug到提交PR实战 🐞
    • Flutter - 船新升级😱支持观察第三方构建的滚动视图💪
    • Flutter - 瀑布流交替播放视频 🎞
    • Flutter - IM保持消息位置大升级(支持ChatGPT生成式消息) 🤖
    • Flutter - 滚动视图中的表单防遮挡 🗒
    • Flutter - 秒杀1/2曝光统计 📊
    • 一天内加入 Flutter 和 FlutterCandies 两大组织是什么体验 🧐
    • Flutter - 如何快速搓一个微信通讯录列表(azlist) 📓
    • Flutter - 混编项目集成Shorebird热更新🐦(安卓篇)
    • Flutter - 混编项目集成Shorebird热更新🐦(iOS篇)
    • Flutter - 解决返回原生页面时dispose方法未被触发的问题 🐞
    • Flutter - 升级3.19之后页面多次rebuild?🤨
    • Flutter - 热更新 Shorebird 1.0 正式版来了 🐦
    • Flutter - 使用Pigeon实现视频缓存插件 🐌
    • Flutter - 轻松搞定屏幕旋转功能 😎
    • Flutter - 解决Connection closed before full header was received
    • Flutter - 实现聊天键盘与功能面板的丝滑切换 🍻
    • Flutter - 支持观察NestedScrollView,兼容性更强 😈
    • Flutter - 聊天键盘与面板丝滑切换的强势升级 🍻
    • Flutter - 升级到3.24后页面还会多次rebuild吗?🧐
    • Flutter - 轻松实现PageView卡片偏移效果
    • Flutter - 轻松搞定炫酷视差(Parallax)效果
    • Flutter - 危!3.24版本苹果审核被拒!
    • Flutter - 子部件任意位置观察滚动数据
    • Flutter - iOS编译加速
    • Flutter - Xcode16 还原编译速度
    • Flutter - GetX Helper 助你规范应用 tag
    • Flutter - GetX Helper 如何应用于旧页面
    • Flutter - 聊天面板库动画生硬?这次让你丝滑个够
    • Flutter - 使用本地 DevTools 验证 SVG 加载优化
    • Flutter - 详情页 TabBar 与模块联动?秒了!
    • Flutter - 详情页初始锚点与优化
    • Flutter - Melos Pub workspaces 实践
      • 一、前言
      • 二、Melos
      • 三、Pub workspaces
        • 优点
        • 缺点
      • 四、迁移优化成效
      • 五、实战
        • 环境要求
        • 初始化仓库
        • 工作区结构
        • 调整 pubspec.yaml
        • 启动
      • 六、最后
      • 资料
  • 移动端
  • Flutter开发
LinXunFeng
2025-10-12
目录

Flutter - Melos Pub workspaces 实践

欢迎关注微信公众号:[FSA全栈行动 👋]

# 一、前言

为解决 App 代码臃肿、编译耗时的问题,我们进行了分包重构,核心思路如下:

  1. 业务分包:将不同业务线的代码拆分成独立的包,开发者只需聚焦于各自包内的 example 工程进行开发,从而提升编译和运行效率。
  2. 功能沉淀:把跨业务复用的功能(包括基础业务和非业务功能)也抽离成独立的包,逐步让主 App 轻量化为一个“空壳”,负责集成所有模块。
  3. 依赖管理:业务包之间使用 git 依赖,指向 master 分支;而非业务的功能包则发布到自建的 unpub 平台,通过版本号管理。
cache:
  version: ">=1.0.0 <2.0.0"
  hosted:
    url: http://unpub.lxf.dev/
package_a:
  git:
    url: git@code.gitlab.com:lxf/package_a.git
    ref: master

分包后虽然利于单个工程的独立开发,但一旦涉及跨包联调,就会变得非常低效。

开发者需要手动修改 dependency_overrides 以 path 方式指定本地依赖,在多个 IDE 窗口间切换,并耗费大量时间处理依赖冲突,这在紧张的工期中尤其痛苦。

因此,在官方的 Pub workspaces 方案出现之前,Melos 的出现有效解决了这些痛点。

# 二、Melos

Melos 是一个管理多个项目的 CLI 工具,只需要在 melos.yaml 中的 packages 下声明各个包,如下所示

# melos.yaml
packages:
  - packages/package_a
  - packages/package_a/example
  - packages/package_b
  - packages/package_b/example

执行 melos bs 即可自动在各个包中创建 pubspec_overrides.yaml 并重写必要的包依赖,顺带执行 flutter pub get。

生成的 pubspec_overrides.yaml 内容如下

# pubspec_overrides.yaml
# melos_managed_dependency_overrides: package_a ...
dependency_overrides:
  # 同步 pubspec.yaml 中的 dependency_overrides
  scrollview_observer: ^1.26.2

  # 如果只依赖了 package_a,则只重写 package_a 依赖
  package_a:
    path: ../../packages/package_a

除此之外,它还提供了脚本功能,方便我们在各个包中去并发执行命令,如下所示

# melos.yaml
scripts:
  fluttergen:
    # 各个包都执行
    exec: fluttergen
  pod_install:
    exec: pod install --project-directory=ios
    packageFilters:
      # 只在各个包的 example 中执行
      scope: "*example"
  iconfont:
    exec: sh ./run.sh
    packageFilters:
      # 只在 package_a 中执行
      scope: package_a
  pub_upgrade:
    run: flutter pub upgrade
    exec:
      # 设置并发数
      concurrency: 1

可配置执行范围,比如只在某个包或各个包的 example 下执行命令;配置执行的并发数等。定义的脚本通过 melos run 去执行,如 melos run iconfont。

还可以做到自动生成 CHANGELOG 和发包到 pub 等,有兴趣的小伙伴可自行到官网了解更多的功能。

尽管 Melos 功能强大,但它仍有两个核心缺陷:

  1. 依赖冲突:由于每个包都维护着独立的 pubspec.lock 文件,Melos 无法从根本上保证所有包的依赖版本一致。
  2. 内存占用:Dart 分析器会为每个包创建独立的分析上下文,导致了额外的内存开销。

# 三、Pub workspaces

在 Dart 3.6.0 的时候,官方带来了 Pub workspaces,解决了上述问题,它的优缺点总结如下

# 优点

优点 描述
统一依赖管理 所有工作区的包共享一个 pubspec.lock 文件,确保依赖版本一致性,避免版本冲突。
性能优化 Dart 分析器为整个工作区创建单一分析上下文,减少内存占用,提升大型仓库的分析性能。
简化操作 只需在仓库根目录运行一次 dart pub get,即可为所有工作区包获取依赖。
自动本地解析 工作区内包之间的相互依赖会自动解析到本地版本,无需手动配置 path 依赖。
灵活的依赖覆盖 支持在根 pubspec.yaml 或 pubspec_overrides.yaml 中进行依赖覆盖。
便捷的命令执行 可以使用 -C 选项在特定工作区包中执行 pub 命令,无需切换目录,如:flutter pub get -C apps/app_a。
清晰的包列表 dart pub workspace list 命令可列出所有工作区包及其路径。

# 缺点

缺点 描述
迁移成本 现有 Monorepo 迁移到 Pub workspaces 需要修改 pubspec.yaml 文件并确保 SDK 约束(至少 ^3.6.0)。
“游离”的 pubspec.yaml 文件 工作区根目录与工作区包之间存在非工作区成员的 pubspec.yaml 文件会导致 pub get 报错。
依赖覆盖限制 一个包只能被覆盖一次。
版本约束匹配 即使使用本地版本,包之间的依赖版本约束仍需匹配。
发布行为差异 工作区包发布到 pub.dev 时,将使用托管版本的依赖,而非工作区内的本地版本。

Pub Workspaces 的一个核心原则:整个工作区只有一个统一的依赖解析。

这意味着在工作区的根目录会有一个全局的 pubspec.lock 和一个全局的 .dart_tool/package_config.json 文件,所有工作区内的包都使用这两个文件来管理它们的依赖。

# 四、迁移优化成效

这是从 Melos 迁移至 Melos + Pub Workspaces 后的提升数据

  • 设备:M1 16G
  • 方式:集成 37 个包(包含 example)并等待代码分析完成

优化前

优化后

优化前 优化后 提升
CPU 时间 9:35.46 7:43.69 19.4%
内存占用 12.23 GB 2.62 GB 78.6%

这里说一点,基于 Pub Workspaces 实现的 Monorepo,Melos 并不是必要的,只是它提供的脚本功能和 Hook 实在是太方便了,有助于提升效率,所以建议搭配使用!

下面我们进入实战

# 五、实战

# 环境要求

Dart 版本需 >=3.6.0,对应 Flutter 版本需 >=3.27.0

全局安装 Melos

dart pub global activate melos "7.0.0-dev.8"

如果你之前有安装过 Melos,可以先卸载再安装

dart pub global deactivate melos

那为什么要指定 7.0.0-dev.8 这个版本?这是因为我们现在使用的 Flutter 版本是 3.29.3,对应的 Dart 版本是 3.7.2,结合 Melos 的当前部分版本要求,如下所示

Version Min Dart SDK Flutter 版本
7.1.1 3.9 3.35.0
7.0.0 3.9 3.35.0
7.0.0-dev.10 3.8 3.32.0
7.0.0-dev.8 3.6 3.27.0

所以只能挑个 7.0.0-dev.8 先用用,如果你已经用上了 Flutter 3.35.0,则可使用当前最新正式版 7.1.1。

# 初始化仓库

创建 workspace 仓库(当然,你也可以基于现有仓库进行改造),名字你随意,这里我以 lxf_workspace 为例。

创建 pubspec.yaml,内容如下

name: lxf_workspace
publish_to: none

environment:
  sdk: ^3.6.0
dev_dependencies:
  # 与全局安装的保持一致
  melos: 7.0.0-dev.8

workspace:
  - apps/app_a
  - packages/package_a
  - packages/package_a/example
  - packages/package_b
  - packages/package_b/example

从 Dart 3.11 开始支持 globs 语法,可以让 pubspec 更加干净,如下所示

workspace:
  - apps/**
  - packages/**

调整所有工程包位置,如壳工程存放至 apps 目录,其它包存放至 packages。

# 工作区结构

.
├── README.md
├── apps
│   └── app_a
├── melos.yaml
├── packages
│   ├── package_a
│   ├── package_b
│   ├── package_c
│   └── ...
├── pubspec.lock
└── pubspec.yaml
文件(夹) 作用
apps 存放壳工程,或其它 app 工程
packages 存放各个仓库的工程,如:业务工程,组件包
pubspec.yaml 声明 workspace,重写依赖,定义 Melos 脚本

# 调整 pubspec.yaml

lxf_workspace 中涉及到的包(即 workspace 下声明的那些),其 pubspec.yaml 都需要做如下两个调整

  • environment.sdk 需 >=3.6.0
  • 新增 resolution: workspace

如下所示

# pubspec.yaml
environment:
 sdk: ">=3.6.0 <4.0.0"
resolution: workspace

注意,这些包可以有 dependency_overrides,但不可以同时对同一个包进行重写,否则会冲突!所以建议将这些重写统一放到 lxf_workspace 的 pubspec.yaml中。

# 启动

执行 melos bs

注意:这里再强调一遍,melos bs 不是应用 Pub Workspaces 的必要流程,使用 Melos 的主要原因是为了使用其脚本功能和 Hook 来提效,如果你不需要这些,也可以直接使用 flutter pub get。

❯ melos bs
melos bootstrap
  └> /Users/lxf/lxf_workspace

Running "flutter pub get" in workspace...
  > SUCCESS

Generating IntelliJ IDE files...
  > SUCCESS

 -> 5 packages bootstrapped

根据上述的 Pub Workspaces 的核心原则,它会将所有包的 pubspec.lock 都删除,所有包都会新增 .dart_tool/pub/workspace_ref.json 并指向根,即 lxf_workspace 目录。

如果你需要更新依赖,则直接在 lxf_workspace 目录下执行 flutter pub upgrade 即可,这些跟原来的一样。

如果你想重写一些第三方库,可以在 pubspec.yaml 中的 dependency_overrides 进行重写

# pubspec.yaml
...
dependency_overrides:
  scrollview_observer: ^1.26.2
  # chat_bottom_container:
  #   path: packages/chat_bottom_container

workspace:
  ...

在完成这些调整后,后续的开发流程还是跟原先一样,只是现在统一在一个 IDE 窗口中操作罢了,这里就不再赘述。

# 六、最后

以上便是基于将所有内容(workspace、apps 和 packages)都上传至一个大型仓库,并统一管理的 Monorepo 方案的实践。

而对于想继续多仓库管理工程包的我来说,还需要对该方案进行改造,因为我觉得分久必合,合久必分是迟早的事,再加上我也比较懒~

好了,下一篇来讲讲我的本地 Monorepo 的 "拼好包" 方案和一些优化。

# 资料

  • https://dart.cn/tools/pub/workspaces (opens new window)
  • melos.invertase.dev/~melos-latest
#Dart#Flutter#Melos#Pub workspaces#Monorepo
上次更新: 2025/10/12, 15:03:08
Flutter - 详情页初始锚点与优化

← Flutter - 详情页初始锚点与优化

最近更新
01
Flutter - 详情页初始锚点与优化
08-24
02
Flutter - 详情页 TabBar 与模块联动?秒了!
08-17
03
Flutter - 使用本地 DevTools 验证 SVG 加载优化
08-07
更多文章>
Theme by Vdoing | Copyright © 2020-2025 FSA全栈行动
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式
×