多端前端:共享包分层、服务态 vs 客户态、平台适配
30 秒导读: Multica 要在网页、桌面(Electron)、手机(React Native)三端上,给同一套"issue / agent / chat"业务复用同一份代码。它的做法是把业务逻辑抽成三个共享包(
ui/core/views),用单向依赖防止它们互相污染;把状态切成服务态(TanStack Query) 和客户态(Zustand) 两半分别托管;再用一层薄薄的平台适配层吸收 Next.js / react-router / Electron 的差异。手机端是特例——它只借类型和纯函数,UI 全自己写。
本章讲前端怎么组织代码,不讲某个页面怎么实现。读完你应该能回答:一段业务逻辑该放哪个包?一个状态该归 Query 还是 Zustand?web 和 desktop 怎么用同一个 <AppLink> 却跳到不同地方?
同组其它章按需相对链接:运行时抽象见 01-agent-runtime,任务状态机见 03-task-dispatch-lifecycle,两套 WebSocket 通道见 04-realtime-protocol;全景与阅读地图见 index。
1. 这是什么(零基础也能懂)
一句话定义: 一个 pnpm monorepo,里面 4 个「客户端」(web / desktop / mobile / CLI)复用同一份「业务大脑」。
要解决的问题: 假设你已经写好了网页版的"看板拖动 issue"逻辑——调哪个 API、乐观更新怎么做、失败怎么回滚。现在产品要出桌面 App 和手机 App。最蠢的做法是复制三份,以后改一个 bug 要改三处、还会漏。Multica 的目标是:业务逻辑只写一次,三端共用;每端只写自己独有的那点"外壳"(桌面的多标签窗口、手机的触屏交互)。
它由哪些部分组成: 一张表看懂顶层布局(依据:pnpm-workspace.yaml、根 CLAUDE.md "Project Shape")。
| 目录 | 角色 | 属于哪端 |
|---|---|---|
packages/ui | 原子 UI 组件(按钮、头像、表格) | 共享 |
packages/core | 无头业务大脑(API client、Query hooks、Zustand store) | 共享 |
packages/views | 共享业务页面(issue 列表、chat 窗口) | 共享(web+desktop) |
apps/web | Next.js 网页壳 | web |
apps/desktop | Electron + react-router 桌面壳 | desktop |
apps/mobile | Expo / React Native 手机壳 | mobile(独立) |
server/cmd/multica | Go 写的命令行客户端 | CLI(第四种客户端) |
一句话直觉: 把它想成一家餐厅。core 是后厨(菜怎么做),ui 是餐具(盘子刀叉),views 是摆好的成品菜;apps/web、apps/desktop 是两个不同门面的店面,共用同一个后厨和成品菜;apps/mobile 是加盟店——只从总部拿菜谱(类型定义),厨房自己重开一个。
本节不碰代码细节。记住一件事:共享靠"分层 + 单向依赖",隔离靠"边界规则"。下面逐层拆。