FlexUI Shadow Sync 详细设计方案

1. 概述

@flexui/shadow-sync 是 FlexUI 的跨后端 Shadow Tree 同步模块,解决 FlexUI 组件树在多个渲染后端之间保持一致性的问题。

1.1 设计目标

  • 跨后端同步:将同一组件树的状态同步到多个后端 (如 DOM Backend + Native Backend)
  • 增量传输:仅传输变更部分,减少通信开销
  • 消息通道:通过抽象 Channel 实现灵活的传输方式
  • 回放支持:支持录制和回放渲染指令序列

1.2 包信息

字段 值
包名 @flexui/shadow-sync
版本 0.1.0
语言 TypeScript
构建 Rollup
入口 dist/flexui_shadow_sync.js / dist/flexui_shadow_sync.es.js

2. 架构设计

┌──────────────────────────────────────────────────┐
│              FlexUI Component Tree                 │
├──────────────────────────────────────────────────┤
│              ShadowSync Backend                    │
│  ┌─────────────┐  ┌──────────────┐               │
│  │ DOM Backend  │  │ Shadow B.    │               │
│  └──────┬───────┘  └──────┬───────┘               │
│         │                  │                       │
│  ┌──────┴──────────────────┴───────┐               │
│  │        Message Channel           │               │
│  │  ┌──────────────────────────┐   │               │
│  │  │  Serialization / Transport│   │               │
│  │  └──────────────────────────┘   │               │
│  └──────────────┬───────────────────┘               │
├─────────────────┼──────────────────────────────────┤
│         Remote Backend (Native / Another Context)   │
│  ┌──────────────┴───────────────────┐               │
│  │  View Controller                 │               │
│  │  ┌────────────┐ ┌──────────────┐ │               │
│  │  │ Element Mgr│ │ Layout Mgr   │ │               │
│  │  └────────────┘ └──────────────┘ │               │
│  └──────────────────────────────────┘               │
└──────────────────────────────────────────────────┘

3. 核心模块

3.1 ShadowSyncElement(同步元素)

ShadowSyncElementType
  ├── Fragment              → 片段节点
  ├── Element               → 标准元素
  ├── TextNode              → 文本节点
  ├── Component             → 组件节点
  ├── ShadowRoot            → Shadow Root
  └── VirtualNode           → 虚拟节点

每个 ShadowSyncElement 有唯一 ID,通过 Channel 与远端对应:

ShadowSyncElement
  ├── _id: number           → 全局唯一 ID
  ├── _slotId: number?      → Slot ID
  ├── __wxElement: Element? → 源 FlexUI Element
  ├── shadowRoot            → Shadow Root 引用
  └── _prepareElement()     → 准备并发送创建指令

3.2 MessageChannel(消息通道)

message_channel.ts (~55KB) 是同步的核心,定义了完整的消息协议:

Channel (抽象接口)
  ├── createElement(id, tagName, stylingName, ownerShadowRoot)
  ├── createTextNode(id, content, ownerShadowRoot)
  ├── setAttribute(id, name, value)
  ├── removeAttribute(id, name)
  ├── addEvent(id, name)
  ├── removeEvent(id, name)
  ├── appendChild(parentId, childId)
  ├── insertBefore(parentId, childId, refId)
  ├── removeChild(parentId, childId)
  ├── setStyle(id, style)
  ├── setTextContent(id, content)
  ├── setSlot(id, slotId)
  └── ...

消息类型:

类别 操作
节点操作 createElement, createTextNode, appendChild, insertBefore, removeChild
属性操作 setAttribute, removeAttribute
样式操作 setStyle, addClass, removeClass
事件操作 addEvent, removeEvent, fireEvent
文本操作 setTextContent

3.3 ViewController(视图控制器)

view_controller.ts (~25KB) 管理远端视图:

ViewController
  ├── 接收 Channel 消息
  ├── 管理 Element 实例
  ├── 维护节点树
  ├── 应用布局计算
  └── 触发渲染刷新

3.4 Backend(同步后端)

backend.ts (~29KB) 实现 composedBackend 接口:

ShadowSync Backend
  ├── domBackend      → 本地 DOM 后端
  ├── shadowBackend   → 远端同步后端
  ├── Channel 管理
  └── ID 生成器

每个元素操作同时在本地 DOM 和远端执行,保持两棵树的状态一致。

3.5 Replay(回放系统)

replay.ts 支持录制和回放渲染指令:

Replay System
  ├── 录制: 记录所有 Channel 指令
  ├── 回放: 按序执行录制的指令
  └── 用途: 调试、测试、离线渲染

3.6 Template Engine(模板引擎桥接)

template_engine.ts 将 Shadow Sync 与 FlexUI 的模板引擎桥接。

3.7 Utils(工具函数)

utils.ts 提供 ID 生成器等基础工具。


4. 通信协议

4.1 最小化传输

Shadow Sync 采用增量同步策略:

  • 仅传输变更的属性和样式
  • 节点操作通过 ID 引用,避免传输完整节点树
  • 批量操作合并为单次传输

4.2 Channel 抽象

Channel 作为抽象层,支持多种传输实现:

  • 进程内:直接函数调用
  • 跨进程:通过 JS Bridge / IPC
  • 跨设备:通过 WebSocket / 网络协议

5. 使用场景

5.1 原生渲染

FlexUI Component (JS)  →  Shadow Sync  →  Native 渲染引擎

组件树在 JS 线程维护,渲染指令通过 Shadow Sync 发送到 Native 线程执行。

5.2 双后端渲染

FlexUI Component (JS)  →  DOM Backend (Web 预览) + Shadow Backend (Native 真机)

同一组件树同时渲染到浏览器 DOM 和 Native 后端。

5.3 远程调试

DevTools Extension  ←  Shadow Sync (回放)  ←  Host App

开发者工具通过回放渲染指令重现宿主应用的组件树。


6. 设计决策

6.1 为什么需要 Shadow Sync?

直接用 @flexui/core 的 composedBackend 会要求 Native 后端在同一个 JS 线程中运行,但实际场景中 Native 渲染通常在独立的 UI 线程。Shadow Sync 通过消息通道解决了这个跨线程/跨进程的同步问题。

6.2 为什么用 ID 引用而非序列化节点树?

  • 性能:传输节点 ID 比传输完整节点树数据量小几个数量级
  • 一致性:两端通过 ID 维护同一棵逻辑树,避免序列化/反序列化的歧义
  • 灵活性:远端可以自行管理节点生命周期和布局

6.3 与 ComposedBackend 的关系

Shadow Sync Backend 实现了 composedBackend 接口:

  • domBackend: 本地 (JS 线程内的) 渲染
  • shadowBackend: 远端 (通过 Channel) 的同步副本

这保持了与核心框架接口的兼容性,上层代码无需感知同步机制。