02 Platform Provider 抽象


02 Platform Provider 抽象

本节摘要:这是第 10 章最精巧的一节。同一个 React 单页应用,要同时跑在浏览器和 Electron 桌面,行为还得一致——怎么做到?靠一层 Platform 抽象:前端不直接调浏览器 API 或 Electron IPC,而是通过 Platform Provider 间接调,这个 Provider 在两个环境有不同实现对上层透明。本节讲清这套抽象,以及它为什么让「一套代码、两个环境」成立。

一、问题:两个环境的差异

浏览器和 Electron 桌面,对前端来说是两个不同的运行环境:

操作 浏览器怎么做 桌面怎么做
打开链接 window.open IPC 调主进程的开链接
重启应用 (浏览器没这概念) IPC 调主进程的重启
通知 浏览器通知 API IPC 调主进程的通知
存储偏好 localStorage 可能走主进程的持久存储
检查更新 (浏览器自动) IPC 调主进程检查更新
fetch 直接 fetch 可能走主进程(desktopFetch,绕 CORS)

如果前端代码里直接写 window.open 或直接调 IPC,会出问题:

  • 写死浏览器 API → 在桌面跑不了(桌面没有 window.open 的预期行为)。
  • 写死 IPC → 在浏览器跑不了(浏览器没 IPC)。
  • 到处 if-else 判断环境 → 代码一团乱。

二、解法:Platform 抽象

OpenWork 的解法是引入一个 Platform 接口,前端通过它访问「环境相关能力」,不直接调具体 API:

Platform 接口(抽象): openLink, restart, notify, storage, checkUpdate, fetch, ... ▼ 浏览器环境实现:用浏览器 API ▼ 桌面环境实现:用 IPC 前端代码:platform.openLink(...) ──► 不关心是哪个环境 ​

也就是说,前端代码只调 platform.某方法,不关心底层是浏览器 API 还是 IPC。Platform 在两个环境有不同的实现,启动时注入对的那个。

三、Platform 接口包含什么

Platform 接口大致含这些:

能力 说明
platform 当前环境("web" / "desktop")
os 操作系统(桌面才有)
capabilities 环境能力集
openLink 打开链接
restart 重启应用
notify 通知
storage 偏好存储
checkUpdate / update 检查/执行更新
fetch 发请求(可能走主进程)
getDefaultServerUrl / setDefaultServerUrl 默认服务端地址管理

这些能力覆盖了「环境相关」的操作。前端需要任何环境相关能力,都从 Platform 拿,不直接调底层。

四、createDefaultPlatform:启动时选实现

Platform 的实现选择发生在启动时。有个 createDefaultPlatform() 函数,根据当前环境创建对的 Platform:

createDefaultPlatform() │ ▼ 判断 isDesktopRuntime()? │ ├─ 是(桌面)──► 创建桌面 Platform(用 IPC 实现) │ ├─ openLink → IPC openDesktopUrl │ ├─ restart → IPC relaunchDesktopApp │ ├─ notify → IPC desktopNotificationShow │ └─ ... │ └─ 否(浏览器)──► 创建 web Platform(用 window API 实现) ├─ openLink → window.open ├─ restart → (不支持 / 刷新页面) ├─ notify → 浏览器通知 └─ ... │ ▼ 用 PlatformProvider 注入 │ ▼ 上层通过 usePlatform() 拿到 ​

注入后,上层用 usePlatform() 钩子拿到 Platform,调它的方法。上层不知道、也不用知道当前是哪个环境。

五、为什么这套抽象让「一套代码、两个环境」成立

这是本节的核心。有了 Platform 抽象:

  • 业务代码一份:session/workspace 等领域模块只调 platform.某方法,一份代码。
  • 环境差异下沉:浏览器/桌面的差异全在 Platform 的两个实现里,对业务不可见。
  • 行为一致:同一个操作(如 openLink)在两个环境都「能做」,只是实现不同——用户体验一致。
领域代码(一份): platform.openLink("https://...") │ ├─ 浏览器: window.open ──► 新标签打开 └─ 桌面: IPC ──► 主进程用系统浏览器打开 │ 结果:都「打开了链接」,用户体验一致 ​

💡 「一套代码、两个环境」的本质:不是「写两份代码分别跑」,而是「一份业务代码 + 两份环境实现」。差异被隔离在环境实现里,业务代码不知道差异存在。

六、加第三个环境有多容易

这套抽象还有个额外红利——加新环境很容易。假设未来要支持移动端:

加移动端 Platform 实现(用移动端 API) │ ▼ createDefaultPlatform() 加分支:移动端 ──► 移动 Platform │ ▼ 业务代码一行不改 ​

因为业务代码只依赖 Platform 接口,不依赖具体环境。加新环境就是加一个新 Platform 实现,业务无感。这是「为未来留缝」的工程智慧(类似 OpenCode 教程第 7 章 Location 为多节点留缝)。

七、capabilities:能力探测

Platform 还带 capabilities(能力集)——声明当前环境支持什么。这让前端能「探测环境能力」:

if (platform.capabilities.hasUpdateCheck) { // 这个环境支持检查更新,显示更新入口 } else { // 不支持,隐藏 } ​

这比「按环境名判断」(if desktop)更优雅——它判断的是「能力」而非「环境」。比如某能力在桌面和某浏览器都有,用 capabilities 判断就都对。

八、本节要点回顾

  1. 两个环境差异:浏览器(window API)vs 桌面(IPC),直接调会绑死环境。
  2. Platform 抽象:前端通过 Platform 接口访问环境能力,不直接调底层。
  3. 接口含:openLink/restart/notify/storage/update/fetch 等。
  4. createDefaultPlatform:启动时按环境选实现,注入 Provider。
  5. 「一套代码两环境」本质:一份业务 + 两份环境实现,差异隔离在实现里。
  6. 加新环境容易:加个 Platform 实现,业务无感——为未来留缝。
  7. capabilities 探测能力:比按环境名判断更优雅。

Platform 讲清了,下一节讲具体领域模块——会话/产物/语音/终端。


作者与出处
原作者: 灏天文库
来源:different-ai
许可证:MIT
整理: 灏天文库整理
由灏天文库结构化整理,提供目录导航、全文检索与在线阅读,便于系统化学习
发布者: 作者: 灏天文库 转发
评论区 (0)
U