Mobx + Electron 还能这样玩?首个基于 MST 的 Electron 跨进程状态同步库

372 阅读4分钟

前言

在现代 Electron 应用开发中,跨进程状态同步是一个绕不开的难题。无论是渲染进程与主进程之间的数据共享,还是多个渲染进程之间的状态同步,传统的 IPC 通信方案往往会导致代码臃肿、维护困难,甚至引发数据一致性问题。

前期调研

在调研社区现有解决方案时,我们发现:

  • electron-redux:基于 Redux,但我们业务的技术栈已全面采用 Mobx,切换成本过高。
  • electron-shared-state:基于 Immer,但功能较为局限,无法满足复杂场景需求。

更重要的是,我们发现社区缺乏基于 Mobx 的跨进程状态同步方案,于是决定从零打造一个专为 Electron 设计的跨进程状态同步库——electron-mst


electron-mst是一个基于 Mobx-State-Tree (MST)  的 Electron 跨进程状态同步库,专为解决多进程状态管理而生。它通过中心化的状态管理、增量更新机制和简洁的 API 设计,让跨进程状态同步变得高效、简洁、可靠。

为什么选择 electron-mst?

  • 高效:利用 Mobx-State-Tree 的增量更新机制,仅传输变化部分,大幅降低 IPC 通信开销。并且 API 设计简单易用(和直接使用 MST 无差别-最简DEMO),接入成本低。
  • 简洁:通过代理模式自动处理状态同步,开发者只需关注业务逻辑,无需手动管理 IPC。
  • 可靠:中心化的状态管理确保多进程间数据一致性,同时处理了进程崩溃、异常操作等边界情况。

无论你也是习惯了使用 Mobx 作为状态管理,或是正在为复杂的 IPC 通信头疼,还是希望找到一种更优雅的状态同步方案,electron-mst 都值得一试。


设计思路

以下是它的设计思路、核心功能以及具体介绍

设计目标

electron-mst 的核心目标是:

  1. 状态共享:在多个渲染进程之间共享统一的状态树(Store),主进程作为状态的中心管理者。
  2. 状态更新同步:任一进程的状态变化能实时同步到其他进程。
  3. 双向通信:支持渲染进程主动请求状态更新,以及主进程推送状态变化。
  4. 按需实例化:Store 实例化按需进行,避免资源浪费。
  5. 高性能:利用 Mobx-State-Tree 的增量更新机制,减少 IPC 通信开销。

功能亮点

  • 状态共享与同步:通过主进程集中管理状态,确保多进程间数据一致性。
  • 增量更新:利用 Mobx-State-Tree 的 Patch 机制,仅传输变化部分,降低 IPC 压力。
  • 按需实例化:Store 实例化延迟到首次使用时,节省内存资源。
  • 易用性:提供简洁的 API,支持 TypeScript 类型推断,降低接入成本。
  • 健壮性:处理了进程崩溃、异常操作等边界情况,确保系统稳定性。

架构设计

electron-mst 采用中心化设计,主进程通过 StoreManager 管理所有 Store 实例的生命周期和状态同步。渲染进程通过 StoreProxy 与主进程通信,确保状态更新的实时性和一致性。

graph
    subgraph 主进程
        M_Store1[Store实例1]
        M_Store2[Store实例2]
        StoreManager
    end

    subgraph 渲染进程A
        RA_Store1_Proxy[[Store实例1-代理]]
        RA_Bridge[Electron MST Bridge]
    end

    subgraph 渲染进程B
        RB_Store1_Proxy[[Store实例1-代理]]
        RB_Bridge[Electron MST Bridge]
    end

    StoreManager -.- M_Store1
    StoreManager -.- M_Store2
    M_Store1 <-->|IPC| RA_Bridge
    RA_Bridge <-.-> RA_Store1_Proxy
    M_Store1 <-->|IPC| RB_Bridge
    RB_Bridge <-.-> RB_Store1_Proxy
---
title: “中心化”模块关系
---
classDiagram
    direction TB
    StoreManager "1" *-- "*" MSTStore : 管理
    MSTStore ..|> IMSTStore

    MSTStore ..> Renderer : 监听Store“action”
    StoreManager ..> Renderer : 监听Store“注册”
    
    class IMSTStore {
    	 <<interface>>
        +syncState(from, to)
    }
    class MSTStore {
        +store : IAnyModelType
        +storeInstance : IAnyStateTreeNode
        +observers : WebContents[]
        +destroy(): void
        +create(snapshot): IAnyStateTreeNode
        +syncSate(from, to): void
    }
    class StoreManager {
        -storeMap : Map
        +init(options) : void
        +destroy() : void
    }

工作机制

  1. 状态同步

    • 渲染进程触发状态更新,通过 IPC 将 Action 发送到主进程。
    • 主进程应用 Action 并生成 Patch,通过 IPC 将 Patch 推送到其他渲染进程。
    • 渲染进程应用 Patch,完成状态同步。
sequenceDiagram
    participant Renderer1 as 渲染进程A
    participant Renderer2 as 渲染进程B
    participant Renderer3 as 渲染进程C
    participant IPCMainProcess as 主进程

    Note over Renderer1: 用户触发状态更新
    Renderer1->>Renderer1: 执行action修改状态
    Renderer1->>IPCMainProcess: 发送action消息

    IPCMainProcess->>IPCMainProcess: 应用动作更新状态
    IPCMainProcess-->>IPCMainProcess: 生成状态更新的Patch
    Note over IPCMainProcess: Patch只含需要更新的部分

    IPCMainProcess-->>Renderer2: 发送Patch
    Renderer2->>Renderer2: 应用Patch更新状态
    IPCMainProcess-->>Renderer3: 发送Patch
    Renderer3->>Renderer3: 应用Patch更新状态
  1. Store 注册

    • 渲染进程首次请求 Store 时,主进程按需实例化 Store 并返回状态快照。
    • 渲染进程应用快照,完成初始化同步。
flowchart LR
    subgraph 渲染进程
        A1[请求注册]
        A2[应用快照]
    end
    subgraph 主进程
        B1{Store 已实例化?}
        B2[实例化 Store]
        B3[返回快照]
    end
    A1 --> B1
    B1 -- 否 --> B2 --> B3
    B1 -- 是 --> B3
    B3 --> A2

技术细节

  • 增量更新:利用 Mobx-State-Tree 的 Patch 机制,仅传输变化部分,大幅降低 IPC 通信开销。
  • 中心化管理:通过 StoreManager 统一管理 Store 实例,避免多进程间状态不一致。
  • StoreProxy:通过代理模式拦截 Store 的 Action 调用,自动触发状态同步,确保数据一致性。
// StoreProxy 关键代码
const StoreProxyHandler = (storeName: string) => ({
    get(target, key, receiver) {
        const value = Reflect.get(target, key, receiver);

        if (
            typeof value === 'object' &&
            isModelType(getChildType(target, key as string))
        ) {
            // 递归代理,使得嵌套的 model 也能触发 action
            return new Proxy(
                value,
                getStoreInstanceHandler(storeName)
            );
        }
        // _isMSTAction: https://github.com/mobxjs/mobx-state-tree/blob/7097c4d6/src/core/action.ts#L41
        if (
            typeof value === 'function' &&
            !!value._isMSTAction
        ) {
            return (...args: any) => {
                try {
                    const res = value.apply(this, args);
                    window.ElectronMST.callAction(
                        storeName,
                        {
                            name: key as string,
                            path: getPath(target),
                            args,
                        }
                    );
                    return res;
                } catch (error: any) {
                    console.error(
                        `[storeInstanceHandler error] ${error?.message}`
                    );
                    throw error;
                }
            };
        }
        return value;
    },
});

落地成效

electron-mst 已在内部业务中成功落地,显著提升了状态同步的效率和代码可维护性。作为首个基于 Mobx-State-Tree 的 Electron 跨进程状态同步库,项目已开源,欢迎交流与贡献:


总结

electron-mst 为 Electron 应用提供了一种高效、简洁、可靠的跨进程状态同步方案。它不仅解决了传统 IPC 通信的痛点,还为复杂业务场景提供了强大的状态管理能力。如果你正在寻找一种更好的 Electron 状态同步方案,不妨试试 electron-mst!

image.png