diff --git a/README.md b/README.md index 6e6e693..44c0ea6 100644 --- a/README.md +++ b/README.md @@ -107,13 +107,23 @@ V1.1 相对 V1.0 的主要变化:第 4 章拆分到 [`prd/modules/`](./prd/mod | [backend/11-cross-domain-collaboration.md](./backend/11-cross-domain-collaboration.md) | 跨域协作与聚合(契约模块、领域事件、并行 fan-out 与局部降级) | | [backend/12-concurrency-and-scheduling.md](./backend/12-concurrency-and-scheduling.md) | 并发、事务与定时任务(事务边界、幂等、乐观锁、ShedLock、本地缓存) | +### tech-selection/ + +对外的技术选型说明材料,写给客户方,可直接作为会议材料使用: + +| 文档 | 内容 | +| --- | --- | +| [tech-selection/前端技术路线评估-Flutter-vs-UniApp.md](./tech-selection/前端技术路线评估-Flutter-vs-UniApp.md) | Flutter / uni-app / uni-app x 三条路线对比,结论**推荐 Flutter**。六章:① 市面跨端方案清单与四条架构路线 ② 三者定位与架构对比 ③ 结合本项目业务的技术评估 ④ 生态、社区与 AI 工具链 ⑤ 构建发布与可扩展性 ⑥ 结论(含成本口径:首版 ±10% 基本持平,差异在五年周期)。含非技术读者可读的通俗解释与术语表 | + +与 `flutter-app/` 的分工:**技术结论仍以 `flutter-app/01-14` 为准**,本目录只做对外解释与论证,不产生新的技术决策。文中所有对 uni-app x 的判断都在附录 A 给出了官方文档链接,便于客户自行复核(核验日期 2026-08-25,日后引用前建议重新抽查)。 + ## 已知文档间差异(已在 PRD V1.0 修订) 以下几处前期材料与 App 架构文档的结论不一致。**以架构文档为准**,[prd/Continental-Retail-APP-PRD.md](./prd/Continental-Retail-APP-PRD.md) 已按架构结论写入并在其第 10 章记录了裁决过程: | # | 差异 | 前期材料 | 实际结论 | V1.0 处置 | | --- | --- | --- | --- | --- | -| 1 | 技术路线 | `Architecture-Diagram/202606-Continental-Retail-APP-PRD.md` 表头写「主技术路线 React Native / 备选 Flutter」 | 实际选型是 **Flutter**,01-14 全部基于 Flutter | ✅ 文档控制表已写 Flutter(PRD C4) | +| 1 | 技术路线 | `Architecture-Diagram/202606-Continental-Retail-APP-PRD.md` 表头写「主技术路线 React Native / 备选 Flutter」 | 实际选型是 **Flutter**,01-14 全部基于 Flutter | ✅ 文档控制表已写 Flutter(PRD C4);对客户的完整论证见 [tech-selection/](#tech-selection) | | 2 | 扫码归属 | 202606 PRD 第 11.5 节 与 `Architecture-Diagram/202606-Conti-Retail-APP-Component-data-source.md` 写成「嵌入 F6 扫码页」 | 扫码是 **App 原生实现**(`native_scan`),同时服务 `feature_scan` 和 H5 的 JSBridge,见 [07](./flutter-app/07-native-integration.md) 和 [10](./flutter-app/10-webview-h5.md) | ✅ PRD REQ-INT-003 已校正(PRD C5) | | 3 | JSBridge 能力数 | 计划稿记 13 项、202606 PRD 记 12 项 | 以 [10](./flutter-app/10-webview-h5.md) 为准:表格 **13 行**,因 `toast`/`dialog`/`loading` 合并为一行,实际 `method` 名共 **15 个** | ✅ PRD 第 7.3 节的 JSBridge 能力清单已按此重述 | diff --git a/tech-selection/前端技术路线评估-Flutter-vs-UniApp.md b/tech-selection/前端技术路线评估-Flutter-vs-UniApp.md new file mode 100644 index 0000000..823ed9d --- /dev/null +++ b/tech-selection/前端技术路线评估-Flutter-vs-UniApp.md @@ -0,0 +1,669 @@ +# 前端技术路线评估:Flutter / uni-app / uni-app x + +| 项目 | 内容 | +| --- | --- | +| 文档名称 | Continental Retail APP 前端技术路线评估 | +| 文档版本 | V1.0 | +| 编制时间 | 2026 年 08 月 | +| 评估对象 | Flutter · uni-app · uni-app x | +| 评估结论 | **推荐 Flutter** | +| 外部数据核验日期 | 2026-08-25(来源见附录 A) | + +文档控制信息 + +--- + +## 0 结论摘要 + +**推荐 Flutter。** + +这不是"哪个框架更时髦"的偏好问题。四条理由,每一条都可以查证: + +**第一,架构路线不同,能力上限就不同。** 跨端方案历史上分四条路线,uni-app 走的是"网页套壳",uni-app x 走的是"编译到系统控件",Flutter 走的是"自绘引擎"。这三条路线各有适用场景——**uni-app 系是为"一套代码同时发小程序 + H5 + App"设计的,而这个项目的目标恰恰是把六套小程序关掉,首版只发 Android 和 iOS。** 我们要付它的代价,收不到它的好处。 + +**第二,这个项目的原生能力密度非常高。** 扫码、VIN 识别、车牌识别、相机、相册、上传进度、定位、权限、弱网离线暂存、崩溃上报、埋点——这些不是画页面,是跟两个操作系统打交道。**这部分工作量不会因为换框架而减少一件。** + +**第三,生态不在一个量级,而且开放程度的差别在"开到哪一层"。** Flutter 主仓库 **91,171 次提交**,uni-app 主仓库 **11,216 次**,约 8 倍差距;fork 数 31,002 对 3,709,同样约 8 倍。更关键的是:Flutter 从渲染引擎到框架层全部 BSD 开源;uni-app x 的组件和 API 虽然也是 Apache-2.0 开源,**但真正决定 App 能力上限的 Runtime、SDK 和 HBuilderX 是 DCloud 自有知识产权,闭源,没有公开的提交历史。** + +**第四,构建链路和扩展能力受限,而"离线打包"这条退路并不是真正的退路。** 想用加密的付费插件,就只能云端打包;想做 Siri 快捷指令这类扩展,官方要求离线打包——**这两个要求互斥。** 更关键的是:离线打包给到的并不是一个可以随便改的原生工程,而是**围着 DCloud 闭源二进制(一组编译好的 AAR)搭起来的壳**,官方文档要求自定义 `Application` 必须继承 `DCloudApplication`,且写明「App离线SDK不支持Kotlin」。**你只能在壳外面加东西,改不了壳里面的东西。** + +**成本结论:首版投入两条路线基本持平,差异在 ±10% 以内。** + +真正的差异不在首版人天,在**五年后**:需求持续增长、要接更多系统级能力时,一条路线的天花板是自己的工程能力,另一条的天花板是厂商插件市场里有没有人卖这个功能。 + +| 维度 | Flutter | uni-app | uni-app x | +| --- | --- | --- | --- | +| 架构路线 | 自绘引擎 | 网页套壳(WebView) | 编译到系统控件 | +| 为谁设计 | 需要跨端一致性的独立 App | 小程序为主、App 为辅 | 小程序团队想做 App | +| 界面一致性 | 两端逐像素一致 | 受各家 WebView 内核影响 | 用系统控件,两端有差异 | +| 嵌 F6 网页 | 通道由 App 侧建立,**F6 零改动** | 同右 | **需 F6 引入 DCloud 的 JS 文件** | +| 原生能力开发 | Pigeon 生成三端强类型代码,支持 CLI | 原生插件(已停止新增上架) | UTS 插件,**只能经 HBuilderX,不支持 CLI** | +| 开源程度 | 引擎 + 框架全开源(BSD) | 框架层开源(Apache-2.0) | 框架层开源,**Runtime / SDK 闭源** | +| 主仓库提交数 | **91,171** | **11,216** | 同左(含在 uni-app 仓库分支) | +| 原生工程归谁 | **自己的工程**,Flutter 挂进去 | DCloud 的壳工程 | **DCloud 的壳工程**,核心是闭源 AAR | +| App 扩展能力 | 标准原生开发,无额外规则 | 受限 | **小组件需 Xcode 做 .appex;Siri 需离线打包** | +| 接入现有 GitLab 流水线 | 原生支持 | — | 结构性错配 | +| 鸿蒙 | 社区分支,落后一段时间 | 支持 | **支持更完整**(这一项 uni-app x 更强) | +| 首版成本 | 基准 | — | **±10% 以内,基本持平** | + +三条路线总览。uni-app(传统版)几格标"—",是因为它在"界面本质上是网页"这一条上就已经不适合本项目,后续章节重点放在 Flutter 与 uni-app x 的对比。每一行的依据见第 1–5 章,外部数据来源见附录 A。 + +--- + +## 1 市面上的跨端方案 + +### 1.1 目前可选的技术栈 + +先把台面上的选项摆全,再谈取舍: + +| 方案 | 出品方 | 语言 | 首次发布 | 现状 | +| --- | --- | --- | --- | --- | +| **原生双端** | Google / Apple | Kotlin · Swift | — | 平台官方方案,能力无上限 | +| **Flutter** | Google | Dart | 2017(1.0 于 2018 年底) | 跨端主流方案,生态最大 | +| **React Native** | Meta | JavaScript / TypeScript | 2015 | 成熟,适合已有 React 团队 | +| **uni-app** | DCloud | Vue + JS | 2018 | 国内多端方案,小程序场景占优 | +| **uni-app x** | DCloud | UTS + UVue | 2023 | uni-app 的原生渲染重构版 | +| **Taro** | 京东 | React / Vue | 2018 | 重心在多家小程序平台 | +| **Kotlin Multiplatform** | JetBrains | Kotlin | 2020 | 逻辑层共享强,UI 层生态尚浅 | +| **.NET MAUI** | Microsoft | C# | 2022 | 微软栈企业适用,国内生态薄 | +| **原生 + H5 混合** | — | 任意 | — | 老牌做法,性能与体验受限 | + +市面上主要的跨端方案。前五个是本次讨论真正需要展开的。 + +### 1.2 归纳:其实只有四条架构路线 + +上面九个方案,剥掉品牌和语言的差异,底层只有四条路线。**理解了这四条,就能理解为什么不同方案的能力上限不一样:** + +``` +① 原生双端(Native) + Android 工程师 ──写一遍──▶ Kotlin ──▶ 安卓系统的按钮 / 列表 / 输入框 + iOS 工程师 ──写一遍──▶ Swift ──▶ 苹果系统的按钮 / 列表 / 输入框 + → 两套代码、两套人,但系统给什么就能用什么,没有天花板 + +② 网页套壳(uni-app、原生 + H5 混合) + 一套 Vue 代码 ──▶ JavaScript ──▶ 系统自带的浏览器组件 ──▶ 渲染成网页 + → 一套代码,但用户看到的本质上是个网页; + 要用系统能力,得靠"插件"从网页世界打一个洞出去 + +③ 编译到系统控件(uni-app x、React Native) + 一套代码 ──编译──▶ Kotlin ──▶ 安卓系统的原生控件 + ──编译──▶ Swift ──▶ 苹果系统的原生控件 + → 一套代码,用户看到的是真正的原生控件。 + 代价:中间那本"翻译词典"必须够全, + 词典里没有的东西就做不出来,得自己去补词条 + +④ 自绘引擎(Flutter) + 一套 Dart 代码 ──▶ Flutter 自带引擎,自己画出每一个像素 ──▶ 屏幕 + → 一套代码,不用系统的任何控件,两端画出来完全一样 +``` + +用装修打比方: + +- **① 原生双端**:请两个装修队分别装两套房子。质量最好,花两份钱,而且两边多半装得不太一样。 +- **② 网页套壳**:在两套房子里各贴一层一模一样的壁纸。快、便宜,但墙还是人家的墙——墙有多平,壁纸就有多平。 +- **③ 编译到系统控件**:拿同一张图纸,翻译成两家各自的材料清单,再让两个装修队照做。比贴壁纸结实得多。**但图纸上画了个东西、翻译词典里没这个词,就做不出来。** +- **④ 自绘引擎**:自己带整套施工队和材料进场,从水泥开始砌。不管进的是哪套房子,装出来完全一样。 + +### 1.3 四条路线的能力上限不一样 + +这是本章最重要的一句话,也是后面所有结论的地基: + +| 路线 | 界面一致性 | 系统能力的获取方式 | 能力天花板由谁决定 | +| --- | --- | --- | --- | +| ① 原生 | 差(两端天然不同) | 直接调 | **无天花板** | +| ② 网页套壳 | 差(受内核影响) | 靠插件从网页打洞出去 | **插件生态有没有人做** | +| ③ 编译到控件 | 中(用系统控件) | 靠"翻译词典" + 插件 | **词典覆盖度 + 插件生态** | +| ④ 自绘引擎 | **最好(逐像素一致)** | 自己写原生插件直接调 | **自己的工程能力** | + +四条路线的对比。最后一列是本项目最该关注的:**②③ 的天花板在别人手里,④ 的天花板在自己手里。** + +一个五年周期的业务系统,需求只会越加越多。天花板在谁手里,是个战略问题,不是技术偏好。 + +--- + +## 2 Flutter 与 uni-app / uni-app x 的对比 + +上一章讲了四条路线。这一章说清楚这三个具体方案分别是哪条路线、为谁设计、适合什么。 + +### 2.1 uni-app:第 ② 条路线,为小程序团队设计 + +**它是什么**:用 Vue 写代码,在 App 端跑在系统自带的浏览器组件里。 + +**它为谁设计**:DCloud 的出发点非常明确——让**已经在做微信小程序的前端团队**,用同一套 Vue 代码顺便发一个 App 和一个 H5。 + +**它适合什么场景**:主战场是小程序、App 只是顺带的项目。比如一个商城,微信小程序是主入口,App 只是给老客户的一个补充。这类项目 uni-app 的价值非常大。 + +**它不适合什么**:App 是主战场、且需要大量系统能力的项目。因为界面本质上是网页,要用相机、要扫码、要后台上传,都得靠插件从网页世界打洞出去。 + +### 2.2 uni-app x:第 ③ 条路线,是 uni-app 的重构版 + +**它是什么**:用一门叫 UTS 的新语言写,编译成安卓的 Kotlin、苹果的 Swift、鸿蒙的 ArkTS,渲染用真正的系统原生控件。 + +**必须澄清一点:uni-app 和 uni-app x 不是同一个东西的两个版本,是两套不同的技术底座。** 它们共享品牌、共享 IDE、共享很多写法,但底层是重写的。一个直接证据:**uni-app x 不支持传统 uni-app 的原生插件,而那批旧原生插件已经停止接受新增上架。** 即使在 DCloud 自家体系内做版本迁移,插件也要重写。 + +**设计思想值得肯定**:UTS 直接编译成 Kotlin 和 Swift,比"字符串方法名 + 弱类型参数"的传统桥接方式要优雅得多。**这一点应该如实承认。** + +**它为谁设计**:还是那批小程序团队,只是这次要把 App 端做得更像原生。 + +**它的约束在哪**:见 1.3 那张表的第 ③ 行——能力天花板取决于"翻译词典"的覆盖度和插件生态。词典里没有的、插件市场上没人卖的,就得自己用 UTS 补,而补这件事又受工具链约束(第 5 章)。 + +### 2.3 Flutter:第 ④ 条路线,为独立 App 设计 + +**它是什么**:Dart 代码提前编译成机器码,界面由 Flutter 自带的引擎逐像素绘制,不使用系统的任何控件。 + +**它为谁设计**:出发点就是**做一个正经的 App**,而不是"顺便也发一个 App"。 + +**这带来两个结构性结果**: + +1. **界面完全脱离系统控件的差异** —— 同一套设计规范在 Android 和 iOS 上落地的结果逐像素一致。 +2. **原生能力靠标准插件机制直接对接** —— 每个能力封装成独立插件包,接口用官方工具从一份定义文件生成 Dart / Kotlin / Swift 三端强类型代码,改了接口忘记同步实现,**编译期就报错**。要什么能力就自己写,不用等插件市场。 + +### 2.4 三者的定位对照 + +| | uni-app | uni-app x | Flutter | +| --- | --- | --- | --- | +| 架构路线 | ② 网页套壳 | ③ 编译到系统控件 | ④ 自绘引擎 | +| 原始设计目标 | 小程序团队顺便发 App | 小程序团队把 App 做好一点 | 做一个独立的正经 App | +| 主平台 | 小程序 / H5 | 小程序 / H5 / App | **App** | +| 界面一致性 | 弱 | 中 | **强** | +| 系统能力获取 | 插件打洞 | UTS 插件 + 翻译词典 | **自写原生插件,直接调** | +| 能力天花板 | 插件生态 | 词典 + 插件生态 | **自己的工程能力** | +| 适合本项目吗 | 否 | 部分适合 | **适合** | + +三个方案的定位差异。最后一行的判断依据在第 3 章。 + +### 2.5 架构层面的结论 + +**这个项目要做的是一个独立 App,不是一个小程序的附属品。** + +它的目标是把六套微信小程序整合掉、关停,首版只发 Android 和 iOS,不发小程序、不发 H5、不发鸿蒙。也就是说: + +> **uni-app 系最值钱的能力——"一套代码同时发小程序、H5 和 App"——在这个项目里没有买家。** + +我们要承担它为了多端兼容而付出的所有架构代价(能力天花板受制于插件生态、工具链绑定、构建方式受限),却收不到它最大的那份好处。 + +**单从架构选型的角度,在这个项目上 Flutter 已经胜出。** 后面三章是把这个结论落到具体的业务需求、生态数据和工程链路上。 + +--- + +## 3 技术评估:结合本项目的业务需求 + +> 这一章写给技术评审。 + +### 3.1 嵌入 F6 网页与双向通道 —— 本项目最高风险项 + +这个 App 里最核心的几条业务链路——报价开单、施工查车、结算收银,以及整个提醒模块——是把 F6 的网页嵌进 App 里承载的。这些网页需要调用 App 的能力(扫码、拍照、相册、带进度的上传、拨号、获取登录态与门店上下文、原生弹窗、跳转原生页等,共 13 项能力、15 个方法),App 也要能反向通知网页。 + +``` +【Flutter 方案】F6 页面不需要任何改动 +──────────────────────────────────────────────── + F6 的网页 + │ window.ContiBridge.call('scan', { mode: 'barcode' }) + │ ▲ + │ └── 这段胶水代码,由 App 在页面加载完成时自动注入 + ▼ + 单一双向通道(JavaScript Channel) + │ + ▼ + App 的 WebView 容器 ──▶ 原生扫码 / 相机 / 上传 + + 一条通道,双向,收发同一套协议:{ id, method, params } + + +【uni-app x 方案】F6 页面必须先引入 DCloud 的 JS 文件 +──────────────────────────────────────────────── + F6 的网页 + │ ⚠ 必须先引入: