Flutter 正在切换成 Monorepo 和支持 workspaces
其实关于 Monorepo 和 workspaces 相关内容在之前《Dart 3.5 发布,全新 Dart Roadmap Update》 和 《Flutter 之 ftcon24usa 大会,创始人分享 Flutter 十年发展史》 就有简单提到过,而目前来说刚好看到 flaux 这个新进展,所以就再展开来聊聊目前 Flutter 里进行中的 Monorepo 和 workspaces。

其实如果你不跑引擎,不提 PR 或者不看源码,那 monorepo 调整对你来说应该没什么正向影响。
monorepo
可能有人还没听说过 monorepo,先介绍下 monorepo(mono repository) ,它是一种项目代码的管理方式,就是将多个项目存储在一个 repo 中,在 monorepo 里多个项目的所有代码都存储在单个仓库里,这个集中式存储库包含 repo 中的所有组件、库和内部依赖项等。
monorepos 也不是什么新概念,很早之前 Google、Meta、Microsoft、 Twitter 等公司都在大规模使用 monorepos ,比如 React 、 Vscode 、Babel 都使用了 monorepo,如下图所示可以看出来他们在形式和结果的区别:

更多可见原文:https://blog.bytebytego.com/p/ep62-why-does-google-use-monorepo
使用 monorepo 的最主要原因之一是简化代码管理,由于所有代码都存储在单个存储库中,因此可以更轻松地跟踪更改、保持版本一致性等,例如 Meta 的工程师曾就表示过:
Meta 的 monorepo 包含公司的大部分代码,可以轻松访问所有代码对开发人员的效率非常重要,工程师可以更深入地了解其依赖关系,调试整个堆栈中的问题,并实施功能或错误修复,所有这些都触手可及······
······版本控制是使用多个存储库时最复杂的问题之一,每个存储库都是独立的,团队可以自由决定要采用的依赖项版本,但是由于每个存储库都按照自己的节奏发展,这些不一致会导致项目可能包含同一依赖项的多个版本,从而导致后续版本控制的各种冲突·····
https://www.growingdev.net/p/what-it-is-like-to-work-in-metas
所以 monorepo 不仅仅只是将所有源代码“紧密放在一起”,更是确保存储库中各个库和应用相互兼容的重要工具:
简单点说人话就是:将现在 Flutter 项目下的
flutter/engine、flutter/buildroot和flutter/flutter这三个存储库合并到单个 flutter/flutter 存储库中。
采用 monorepo 对于目前 Flutter 来说有着许多好处,例如更方便的 CI 和更容易协作,同时提高代码共享的能力,例如在现在的 flutter/flutter 项目下,由于 flutter/flutter 和 flutter/engine 是两个独立项目,所以经常可以看到一堆”无意义“的 Roll Engine PR 的记录存在:

“Roll Flutter Engine from····” 主要是为了更改 framework 引用的 engine 的版本,每次 engine 有新的提交的时候,都会在 framework 自动创建一个 PR,更新 engine ,当所有测试通过时,PR 就会自动合并。
而如果使用 monorepo , flutter/flutter 和 flutter/engine 将不再是相互隔离的 repo ,在 CI 和版本管理上都会更轻便可控,例如一开始文章提到的 flutter/flaux ,就是目前 Flutter 正在测试合并流程的项目,在最终合并到主flutter/flutter 之前验证 monorepo 的仓库。
也就是
flutter/engine这个 repo 最终会在未来的某个时间点被 Archive ,虽然这和 Flutter 以前解释说不适用 monorepo 的原因相违背, 但是这个阶段来看,Flutter 还是需要选择 monorepo。
对于 monorepo 而已,所有依赖都是一个来源,所以意味着不存在版本冲突和依赖地狱,同时 monorepo 还支持原子提交,在大规模重构里开发人员可以在一次提交中更新多个包或项目。

其实目前 pub 上很多项目都在使用类似 monorepo 的结构,例如 riverpod 、cfug/dio 、ubuntu/app-center 、Flame-Engine/Flame 等项目都在使用 melos 做 monorepos 管理,而 Flutter 这次也是通过自定义对应的 CI 基础能力来更新以支持合并后的 repo 构建,也就是 Flutter 针对 monorepo 结构自定义了一套新的 CI 系统,事实上 monorepo 核心之一,就是在于 CI 的搭建还有项目结构分层的处理上。
更多可见:https://melos.invertase.dev/~melos-latest/#projects-using-melos 、https://github.com/orgs/flutter/projects/182/
总结
总结起来,Flutter 迁移到 monorepo 的好处在于:
-
提交原子性:engine、framework 和 buildroot 的变更可以一起提交,更好管理
-
减少依赖 :可以减少大量内部版本依赖和冲突问题,大规模减少类似 Roll 类型的历史提交等
-
简化 PR:用户可以更直观和方便提交 PR,包括这个 PR 需要从 engine 到 framework 多方调整的时候
-
更方便测试: engine 和 framework 可以在同套 CI 下测试调整
当然 monorepo 也带来了新的问题,例如一个 repo 下多个 Dart 项目的解析问题,这就不得不说下面的 workspaces 概念。
workspaces
在 Dart 3.5 的时候,Dart Roadmap 就提出了 pub workspaces 概念,核心就是在 monorepo 中实现多个相邻包的共享解析,比如「共享解析」可以解决类似编辑器中的 Analyzer 占用空间过大问题,因为它可以减少单独的上下文 context。
analysis_options.yaml 下的大量规则引入也会增加内存上涨,事实上 Dart Analyzer 的上下文数量一直是内存消耗大户。
在 dart#53874 提到过,由于 monorepo 结构化的原因,Analyzer 在工作的时候最终会为每个包及其所有依赖项加载了多个重复的 analysis contexts,从而导致 monorepo 里每个包 analysis 时在内存中生成了多个副本,最终出现内存占用过大问题:


而解决方案就是在这些 repo 中为每个依赖项创建一个共享解决方案,也就是这里提到的 pub workspaces ,通过 workspaces ,项目在 monorepo 中可以实现多个相邻包的共享解析,通过共享 Analyzer 等工具在分析每个包之间共享上下文,从而节省大量内存。
也就是 workspaces 可以让 monorepo 创建共享版本来优化依赖项解析速度和内存。
例如,你可以通过 workspace 关键字启用 workspaces 并引入对应的 packages 结构:
name: workspace
environment:sdk: ^3.5.0
workspace:- packages/package_a- packages/package_b
然后在 packages/package_a/pubspec.yaml 和 packages/package_b/pubspec.yaml 添加一行 resolution: workspace:
name: package_a
environment:sdk: 3.5.0
resolution: workspacedependencies:# You can depend on packages inside the workspace:package_b: ^1.2.4 # This version constraint will be checked against the local version
dev_dependencies:test: ^1.0.0
此时你的项目就已经处于 workspace 的工作模式下,而 workspaces 主要是支持 workspaces 下 monorepo 的包之间的共享解析,例如在仓库中的任意位置运行 flutter pub get ,项目会将产生一个共享解析,具体表现为:只会在 root pubspec.yaml 同级目录生成 一个 root pubspec.lock 。

另外,通过 dart pub deps 也可以更清晰看到整个 workspace 的依赖关系,也可以更好管理和同步依赖,结合 monorepo 结构或者 melos 等工具的能力,会让整个项目更直观可控:

最后
可以看到 monorepo 和 workspaces 属于相辅相成的存在,而 monorepo 也不是单纯就是把东西放到一个 repo ,更多涉及项目结构的分层,包的细化还有最关键的 CI 工具支持等,同时 monorepo 也可以更好管理所有项目的依赖关系,甚至支持增量构建等等。
总的来说 monorepo 并不是什么新鲜东西,只是到了这个阶段 Flutter 所需要做的一个调整,之前采用 multi repo 是规划里把多个 repo 当作独立产品,而现在是作为一个整体项目来管理的情况下,自然选择 monorepo 更合适。
参考资料
-
https://github.com/dart-lang/sdk/issues/53875
-
https://github.com/dart-lang/pub-dev/pull/7762
-
https://github.com/dart-lang/pub/issues/4391
-
https://github.com/dart-lang/pub/issues/4127
-
https://docs.google.com/document/d/1UEEAGdWIgVf0X7o8WPQCPmL4LSzaGCfJTM0pURoDLfE/edit?resourcekey=0-c5CMaOoc_pg3ZwJKMAM0og&tab=t.0
-
https://github.com/flutter/engine/blob/main/pubspec.yaml
-
https://github.com/orgs/flutter/projects/182
-
https://github.com/flutter/flaux
-
https://github.com/flutter/cocoon
相关文章:
Flutter 正在切换成 Monorepo 和支持 workspaces
其实关于 Monorepo 和 workspaces 相关内容在之前《Dart 3.5 发布,全新 Dart Roadmap Update》 和 《Flutter 之 ftcon24usa 大会,创始人分享 Flutter 十年发展史》 就有简单提到过,而目前来说刚好看到 flaux 这个新进展,所以就再…...
小白初入Android_studio所遇到的坑以及怎么解决
1. 安装Android_studio 参考:Android Studio 安装配置教程 - Windows(详细版)-CSDN博客 Android Studio超级详细讲解下载、安装配置教程(建议收藏)_androidstudio-CSDN博客 想下旧版本的android_studio的地址(仅供参考…...
NetCore使用Aop和内存缓存对接口、方法进行数据缓存
通过Aop内存缓存对接口、方法进行缓存 源码地址https://gitee.com/wangbenchi66/nuget 1. nuget包引入 必须引入包 至少在2024.11.7以上 <PackageReference Include"WBC66.Cache.Core" Version"2024.11.7" />必须开启内存缓存 否则后续步骤无法正…...
playwright学习记录2--定位方式
快捷导航 定位方式:元素操作断言方式自动等待 定位方式: csspage.get_by_role() 通过显式和隐式可访问性属性进行定位。page.get_by_text() 按文本内容定位。page.get_by_label() 通过关联标签的文本定位表单控件。page.get_by_placeholder() 通过占位符…...
响应式网页设计--html
一,HTML 文档的基本结构 一个典型的 HTML 文档包含了几个主要部分,基本结构如下(本文以下出现的所有代码都可以套入下面示例进行测试): <!DOCTYPE html> <html lang"zh"> <head><meta charset"UTF-8&q…...
C#核心(8) 静态成员
前言 先前我们已经学习了成员变量以及成员属性。 静态成员对于在整个应用程序中共享数据和功能非常有用。它们可以用于跟踪全局状态、共享常量和实现单例模式等。但是需要注意的是,过度使用静态成员可能导致代码变得难以维护和测试,因此应谨慎使用。其…...
关于git使用的图文教程(包括基本使用,处理冲突问题等等)超详细
目录 用户签名,初始化git git提交流程图 提交到本地库 版本穿梭 分支操作 分支合并冲突 团队协作 github的使用 推送代码 克隆 拉取代码 团队协作冲突 团队协作之分支管理 推送分支到分支: 拉去远程库分支到本地库: 本地删除远程分支&am…...
Axios 的 responseType 属性详解及 Blob 与 ArrayBuffer 解析
在前端开发的广阔天地中,Axios 犹如一颗璀璨的明星,为我们与服务器之间的通信搭建起坚实的桥梁。其中,responseType 属性更是赋予了我们灵活处理服务器响应的强大能力。 一、Axios 的 responseType 属性值及示例 1.arraybuffer 当我们将 r…...
redis集群介绍
1. 节点(Node): • Redis集群中的单个Redis服务器实例。每个节点都运行一个Redis服务器进程,并维护自己的数据。 2. 分片(Sharding): • 将数据集分割成多个部分,并分布到不同的节点…...
JDK中常用的包有哪些?
1.java.lang 描述:包含Java语言的核心类,不需要显式导入。 常用类:Object、String、Math、System、Thread、Exception等。 2.java.util 描述:提供了集合框架、日期和时间功能、随机数生成、扫描和格式化等实用工具类。 常用类…...
校园官网练习---web
HTML: <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>西安工商学院</title><…...
MySQL中指定字段的某个值排在前面
一 需求 如果我们想讲表中指定的字段的某一个值排序在最前面应该如何处理? 二 实现方式 方法 1、使用<>,xml中使用<![CDATA[跳过解析的特殊符号]]>或者<(小于符号)>(大于符号) ORDER …...
【51单片机】I2C总线详解 + AT24C02
学习使用的开发板:STC89C52RC/LE52RC 编程软件:Keil5 烧录软件:stc-isp 开发板实图: 文章目录 AT24C02介绍存储器 I2C总线介绍I2C时序结构数据帧AT24C02数据帧 编程实例 —— 按键控制数据大小&存储器写入读出 AT24C02介绍 …...
直接插入排序法
任务描述 本关任务:要求编写程序实现直接插入排序的功能。 相关知识 直接插入排序,是指将无序序列中的各元素依次插入到已经有序的数组中。 假设数组中前 i-1 元素已经有序,现在要将线性表中第 i 个元素插入到前面的有序子表中,…...
mysql中InnoDB索引与MyISAM索引
mysql索引 InnoDB 索引存储 主键索引(聚簇索引) 定义:主键索引是 InnoDB 存储引擎的聚簇索引,它决定了表中数据的物理存储顺序。每个 InnoDB 表都有一个且仅有一个聚簇索引。存储:主键索引的叶子节点直接包含表的数…...
Redis如何保证数据不丢失(可靠性)
本文主要以学习为主,详细参考:微信公众平台 Redis 保证数据不丢失的主要手段有两个: 持久化 多机部署 我们分别来看它们两的具体实现细节。 1.Redis 持久化 持久化是指将数据从内存中存储到持久化存储介质中(如硬盘…...
【计网】物理层学习笔记
【计网】物理层 物理层概述 物理层要实现的功能 在各种传输媒体上传输比特0和1,进而为上面的数据链路层提供透明传输比特流的作用。 物理层接口特性 物理层之下的传输媒体 传输媒体是计网设备之间的物理通路,也称为传输介质。 传输媒体并不包含在…...
vue链接跳转
在 Vue 3 的组合式 API 中,你可以使用 ref 和 setup 函数来实现外部链接跳转功能。 方法 1:使用 click 和 window.open(新标签页跳转) 这种方式在点击时会打开一个新标签页并跳转到外部链接。 <menu-item value"item2&…...
IP地址是电脑自带的吗?是根据什么而决定的
IP地址并非电脑自带,而是由网络运营商或网络服务提供商通过特定的协议和机制进行分配和管理的。要深入理解IP地址的来源和决定因素,我们需要从IP地址的基本概念、分配方式以及影响分配的因素等多个方面进行探讨。 IP地址,即互联网协议地址&am…...
JavaFX史上最全教程 - Shape - JavaFX矩形椭圆
avaFX Shape类定义了常见的形状,如线,矩形,圆,Arc,CubicCurve,Ellipse和QuadCurve。 在场景图上绘制矩形需要宽度,高度和左上角的(x,y)位置。 要在JavaFX中…...
地震勘探——干扰波识别、井中地震时距曲线特点
目录 干扰波识别反射波地震勘探的干扰波 井中地震时距曲线特点 干扰波识别 有效波:可以用来解决所提出的地质任务的波;干扰波:所有妨碍辨认、追踪有效波的其他波。 地震勘探中,有效波和干扰波是相对的。例如,在反射波…...
ubuntu搭建nfs服务centos挂载访问
在Ubuntu上设置NFS服务器 在Ubuntu上,你可以使用apt包管理器来安装NFS服务器。打开终端并运行: sudo apt update sudo apt install nfs-kernel-server创建共享目录 创建一个目录用于共享,例如/shared: sudo mkdir /shared sud…...
Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility
Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility 1. 实验室环境1.1 实验室环境1.2 小测试 2. The Endor System2.1 部署应用2.2 检查现有策略 3. Cilium 策略实体3.1 创建 allow-all 网络策略3.2 在 Hubble CLI 中验证网络策略源3.3 …...
Linux云原生安全:零信任架构与机密计算
Linux云原生安全:零信任架构与机密计算 构建坚不可摧的云原生防御体系 引言:云原生安全的范式革命 随着云原生技术的普及,安全边界正在从传统的网络边界向工作负载内部转移。Gartner预测,到2025年,零信任架构将成为超…...
前端开发面试题总结-JavaScript篇(一)
文章目录 JavaScript高频问答一、作用域与闭包1.什么是闭包(Closure)?闭包有什么应用场景和潜在问题?2.解释 JavaScript 的作用域链(Scope Chain) 二、原型与继承3.原型链是什么?如何实现继承&a…...
[免费]微信小程序问卷调查系统(SpringBoot后端+Vue管理端)【论文+源码+SQL脚本】
大家好,我是java1234_小锋老师,看到一个不错的微信小程序问卷调查系统(SpringBoot后端Vue管理端)【论文源码SQL脚本】,分享下哈。 项目视频演示 【免费】微信小程序问卷调查系统(SpringBoot后端Vue管理端) Java毕业设计_哔哩哔哩_bilibili 项…...
Python Einops库:深度学习中的张量操作革命
Einops(爱因斯坦操作库)就像给张量操作戴上了一副"语义眼镜"——让你用人类能理解的方式告诉计算机如何操作多维数组。这个基于爱因斯坦求和约定的库,用类似自然语言的表达式替代了晦涩的API调用,彻底改变了深度学习工程…...
人工智能--安全大模型训练计划:基于Fine-tuning + LLM Agent
安全大模型训练计划:基于Fine-tuning LLM Agent 1. 构建高质量安全数据集 目标:为安全大模型创建高质量、去偏、符合伦理的训练数据集,涵盖安全相关任务(如有害内容检测、隐私保护、道德推理等)。 1.1 数据收集 描…...
手机平板能效生态设计指令EU 2023/1670标准解读
手机平板能效生态设计指令EU 2023/1670标准解读 以下是针对欧盟《手机和平板电脑生态设计法规》(EU) 2023/1670 的核心解读,综合法规核心要求、最新修正及企业合规要点: 一、法规背景与目标 生效与强制时间 发布于2023年8月31日(OJ公报&…...
关于easyexcel动态下拉选问题处理
前些日子突然碰到一个问题,说是客户的导入文件模版想支持部分导入内容的下拉选,于是我就找了easyexcel官网寻找解决方案,并没有找到合适的方案,没办法只能自己动手并分享出来,针对Java生成Excel下拉菜单时因选项过多导…...
