04 desktopBridge 代理


04 desktopBridge 代理

本节摘要:这是第 10 章的收尾。桌面环境下,前端要调主进程的 IPC(第 9 章),但裸调 IPC 写起来繁琐。OpenWork 用一个 desktopBridge Proxy——把对主进程的调用包装成「像调方法一样」的代理对象。本节讲清这个 Proxy 的工作原理,以及它如何与第 9 章的 IPC 命令注册表对接。

一、回顾:桌面环境要调主进程

第 9 章讲过,桌面环境下前端有些操作要走主进程(窗口、文件、运行时、通知等)。这些操作通过 IPC 命令调用:

前端: invokeDesktop("openDialog", { ... }) │ ▼ IPC 到主进程 │ 主进程: 找到 "openDialog" 命令执行 │ ▼ 结果返回 ​

但这种「invokeDesktop(命令名, 参数)」的写法,用起来有点繁琐——每次都要写字符串命令名,IDE 也不补全。

二、desktopBridge Proxy:像调方法一样

desktopBridge Proxy 把这种调用包装成代理对象,你访问它的属性就像「调方法」:

// 没有 Proxy 的写法(繁琐): await invokeDesktop("openDialog", { ... }); await invokeDesktop("readFile", { path: "..." }); await invokeDesktop("restartEngine"); // 有 Proxy 的写法(优雅): await desktopBridge.openDialog({ ... }); await desktopBridge.readFile({ path: "..." }); await desktopBridge.restartEngine(); ​

两种写法等价,但后者读起来像「直接调方法」,更清晰、IDE 能补全(因为类型来自第 9 章的共享类型包)。

三、Proxy 的工作原理

desktopBridge 是一个 Proxy 对象(JavaScript 的 Proxy 特性)。它的魔法在于 get 拦截——你访问任何属性,它都返回一个函数,这个函数把属性名当命令名调 IPC:

// 概念性示例:desktopBridge Proxy 的实现 const desktopBridge = new Proxy({}, { get(target, prop) { // 缓存:如果之前生成过这个方法,直接用 if (target[prop]) return target[prop]; // 否则生成一个函数:把 prop 当命令名调 IPC target[prop] = (...args) => invokeDesktop(prop, ...args); return target[prop]; } }); ​

所以 desktopBridge.openDialog(...) 实际是 invokeDesktop("openDialog", ...),desktopBridge.readFile(...) 是 invokeDesktop("readFile", ...)。Proxy 自动把「属性访问」翻译成「IPC 命令调用」。

💡 Proxy 的价值:它让「调 IPC」变得像「调本地方法」——代码清晰、IDE 友好、不用记字符串命令名。这是「用语言特性消除样板代码」的好例子。

四、与共享类型包对接:类型安全

desktopBridge 的类型来自第 9 章的共享类型包(DesktopCommandMap)。这个类型定义了所有可用命令及其参数/返回类型:

// 共享类型包定义(概念性) type DesktopCommandMap = { openDialog: { args: {...}, result: {...} }; readFile: { args: { path: string }, result: string }; restartEngine: { args: void, result: void }; ... }; // desktopBridge 的类型就是这个映射 type DesktopBridge = { [K in keyof DesktopCommandMap]: (args: DesktopCommandMap[K]["args"]) => Promise<DesktopCommandMap[K]["result"]>; }; ​

所以 desktopBridge.readFile({ path }) 的参数和返回都有类型检查——传错参数编译报错。这是第 9 章强校验契约在前端的体现。

五、desktopBridge 放在哪:框架无关层

经核实,desktopBridge 的实现在 app/ 框架无关层(不是 react-app/)。这符合第 01 节的分层规则——IPC 客户端是「纯逻辑」,不依赖 react,所以放框架无关层。

src/app/lib/desktop.ts ← desktopBridge 实现在这里(框架无关层) │ ▼ 被内核/领域引用 │ ▼ 但通过 Platform 抽象访问(第 02 节),不直接 import ​

注意:虽然 desktopBridge 在框架无关层,但领域通常不直接 import 它——而是通过 Platform 抽象间接用。这保持了「领域不直接依赖具体实现」的分层纯洁。

六、desktopFetch:Proxy 的另一面

desktopBridge 还暴露一个 desktopFetch(第 9 章提过)——让前端能走主进程发跨域请求:

// 回环地址直发,跨域走主进程 if (isLoopback(url)) { return fetch(url); // 直发(快) } else { return desktopBridge.__fetch(url); // 走主进程(绕 CORS) } ​

desktopFetch 内部判断:回环直发(性能),跨域走主进程(绕限制)。这是 desktopBridge 提供的「特殊 IPC 用法」。

七、第 10 章收尾:前端的全貌

读完第 10 章四节,你掌握了 OpenWork 前端的完整图景:

节 讲了什么
01 三层分层 app(逻辑)/ kernel(Provider)/ shell(骨架)/ domains(业务)
02 Platform 抽象 一份业务 + 两份环境实现,跨浏览器与桌面
03 领域模块 session/workspace 等,通过 Provider 访问后端
04 desktopBridge Proxy 把 IPC 包装成方法调用 + 类型安全

核心认知:前端用「严格分层 + Platform 抽象 + Provider 访问 + Proxy 包装」实现了「一套代码跨环境、可维护、类型安全」。第 11 章我们看前端如何发起「分享」——Connect Link。

本节要点回顾

  1. desktopBridge Proxy:把 IPC 调用包装成「像调方法」的代理对象。
  2. 工作原理:Proxy 的 get 拦截,把属性访问翻译成 invokeDesktop(命令名, ...)。
  3. 价值:代码清晰、IDE 友好、不用记字符串命令名。
  4. 类型安全:类型来自共享类型包(DesktopCommandMap),编译期校验。
  5. 放在框架无关层(app/lib):IPC 客户端是纯逻辑,不依赖 react。
  6. 领域间接用:通过 Platform 抽象访问,不直接 import desktopBridge。
  7. desktopFetch:回环直发、跨域走主进程,Proxy 的特殊用法。
  8. 第 10 章结束:前端全貌——分层+Platform+Provider+Proxy。

第 10 章结束。下一章讲 Connect Link——连接分享与多端接入。


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