Claude Code Plugins

Community-maintained marketplace

Feedback

reactumg-architecture

@15195999826/LomoMarketplace
0
0

ReactUMG 架构原理深度解析。仅供 PlanReactUMG 和 DebugReactUMG Agent 显式调用,不应在日常开发中直接激活。包含三层架构、Reconciler、hostConfig 等底层实现机制。

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name reactumg-architecture
description ReactUMG 架构原理深度解析。仅供 PlanReactUMG 和 DebugReactUMG Agent 显式调用,不应在日常开发中直接激活。包含三层架构、Reconciler、hostConfig 等底层实现机制。

ReactUMG 架构原理深度解析

本文档详细说明 ReactUMG 插件如何将 TypeScript 编写的 React 组件转换为 UE5 的 UMG Widget。


1. 核心思想

ReactUMG = React Reconciler + Puerts + UMG

ReactUMG 是一个桥接层,让开发者可以使用 React 的方式编写 UE5 UI,同时保持原生性能和完整类型安全。

关键特性

  • 完全拥抱 React 生态 - 标准 react-reconciler,支持 Hooks、Context、Ref
  • 零性能开销 - Puerts 直接操作 C++ 内存,无序列化/反序列化
  • 极简 C++ 层 - 只有 2 个类 + 5 个函数
  • 完整类型安全 - 2406 行类型定义
  • 自动事件管理 - 自动检测 Delegate 类型,自动绑定/清理

2. 三层架构

┌─────────────────────────────────────────────────────────┐
│  TypeScript Layer (开发者编写层)                          │
│  ─────────────────────────────────────────────────────  │
│  - 开发者编写 React.Component                            │
│  - 使用 JSX 语法描述 UI 结构                              │
│  - 调用 ReactUMG.render() 渲染                           │
│  - 管理状态和业务逻辑                                      │
└───────────────────────┬─────────────────────────────────┘
                        │
                        ▼
┌─────────────────────────────────────────────────────────┐
│  React-Reconciler Layer (协调层,核心逻辑)               │
│  ─────────────────────────────────────────────────────  │
│  - hostConfig 定义如何操作 Widget                        │
│  - UEWidget 包装 UE::Widget                             │
│  - UEWidgetRoot 包装 UReactWidget                       │
│  - 处理 VDOM diff/update/mount/unmount                  │
│  - 管理 Widget 树的生命周期                               │
└───────────────────────┬─────────────────────────────────┘
                        │ Puerts Bridge (JS ↔ C++)
                        ▼
┌─────────────────────────────────────────────────────────┐
│  C++ Layer (UE5 原生层)                                  │
│  ─────────────────────────────────────────────────────  │
│  - UReactWidget (根容器 UserWidget)                     │
│  - UUMGManager (蓝图函数库,工具函数)                     │
│  - UWidget::SynchronizeProperties() (同步到 Slate)      │
└─────────────────────────────────────────────────────────┘

各层职责

TypeScript Layer(开发者层)

  • 职责:提供 React 开发体验
  • 工具:React 组件、JSX、Hooks、State 管理
  • 入口ReactUMG.render(<MyComponent />)

React-Reconciler Layer(协调层)

  • 职责:将 React VDOM 映射到 UMG Widget 树
  • 核心hostConfig 定义 Widget 操作,UEWidget 包装原生 Widget
  • 优势:使用标准 react-reconciler,兼容 React 生态

C++ Layer(原生层)

  • 职责:提供最小化的容器和工具函数
  • 设计:极简设计,只有 2 个类
  • 集成:与 UE5 UMG 系统无缝对接

3. 核心组件详解

C++ 层组件

UReactWidget(根容器)

文件位置Plugins/ReactUMG/Source/ReactUMG/ReactWidget.h/.cpp

职责

  • 继承 UUserWidget,作为整个 React UI 的根容器
  • 单根限制:只允许一个 RootSlot,对应 React 的单根要求
  • 持有 WidgetTree,管理整个 Widget 树
  • 提供 AddChild/RemoveChild 供 TypeScript 调用

UUMGManager(工具函数库)

文件位置Plugins/ReactUMG/Source/ReactUMG/UMGManager.h/.cpp

职责

  • 蓝图函数库,暴露静态函数给 TypeScript 调用
  • CreateReactWidget():创建 UReactWidget 实例
  • SynchronizeWidgetProperties():同步 Widget 属性到 Slate 层
  • SynchronizeSlotProperties():同步 Slot 属性到 Slate 层

为什么需要 Synchronize?

UE5 的 UMG 是双层架构

  1. UWidget(逻辑层)- C++ 对象,存储属性
  2. SWidget(Slate 渲染层)- 实际渲染的组件

当通过 TypeScript 修改 UWidget 的属性时:

  • 使用 puerts.merge 直接修改 C++ 对象的内存
  • UWidget 的属性值已改变,但 SWidget 还未更新
  • 必须调用 SynchronizeProperties() 将改动同步到 Slate 层

TypeScript 层组件

UEWidget(Widget 包装类)

文件位置Plugins/ReactUMG/TypeScript/react-umg/react-umg.ts:35-159

职责

  • 包装 UE::Widget,提供统一的操作接口
  • 管理属性设置、事件绑定、子节点操作
  • 处理 Slot 延迟设置(Slot 由父容器创建)
  • 自动清理事件监听,防止内存泄漏

UEWidgetRoot(根容器包装)

文件位置Plugins/ReactUMG/TypeScript/react-umg/react-umg.ts:161-192

职责

  • 包装 UReactWidget,提供 TypeScript 访问接口
  • 管理根 Widget 的添加/移除
  • 控制 Widget 在视口中的显示/隐藏

hostConfig(React Reconciler 配置)

文件位置Plugins/ReactUMG/TypeScript/react-umg/react-umg.ts:195-283

职责

  • 定义 React Reconciler 如何操作 Widget 树
  • 处理 Widget 的创建、更新、删除、挂载
  • 连接 React VDOM 和 UMG Widget 树

4. 完整更新流程

渲染流程(TypeScript → UMG)

1. 开发者调用:
   ReactUMG.render(<TaleSelectPanel />)
   ↓
2. React Reconciler 递归处理 VDOM:
   hostConfig.createInstance("CanvasPanel", props)
   → new UEWidget("CanvasPanel", props)
   → new UE.CanvasPanel()
   ↓
3. 构建 Widget 树结构:
   hostConfig.appendInitialChild(parent, child)
   → parent.appendChild(child)
   → parent.nativePtr.AddChild(child.nativePtr)
   ↓
4. 应用 Widget 属性:
   puerts.merge(widget.nativePtr, {Text: "Hello"})
   → 直接修改 C++ 对象属性
   ↓
5. 同步属性到 Slate 层:
   UE.UMGManager.SynchronizeWidgetProperties(...)
   → widget->SynchronizeProperties()
   → Slate 层更新
   ↓
6. 显示到屏幕:
   hostConfig.resetAfterCommit(root)
   → root.addToViewport(0)

更新流程(Props 变化)

1. 状态变化触发:
   this.setState({count: 2})
   ↓
2. Reconciler Diff Props:
   hostConfig.prepareUpdate(...)
   → !deepEquals(...) → return true
   ↓
3. 提交更新:
   hostConfig.commitUpdate(instance, ...)
   → instance.update(oldProps, newProps)
   → puerts.merge(nativePtr, {Text: "2"})
   → UE.UMGManager.SynchronizeWidgetProperties(...)
   ↓
4. 屏幕更新:
   Slate 层重新渲染

事件流(UMG → TypeScript)

1. 初始化时绑定事件:
   <Button OnClicked={this.handleClick} />
   → UEWidget.bind("OnClicked", this.handleClick)
   → nativePtr.OnClicked.Add(this.handleClick)
   ↓
2. 用户交互触发事件:
   用户点击按钮
   → UButton::OnClicked.Broadcast()
   ↓
3. Puerts 桥接转发:
   C++ Delegate.Broadcast()
   → Puerts 拦截并转发到 JS
   → this.handleClick() 在 TypeScript 中执行
   ↓
4. TypeScript 处理事件:
   handleClick() { this.setState({clicked: true}) }
   → 触发 Re-render

5. 完整更新流程(4 阶段)

阶段 0:触发源(用户代码)
├── setState() - 状态更新
├── forceUpdate() - 强制更新
├── Context 变化 - Provider value
└── ReactUMG.render() - 初次挂载
    ⚠️ 私有成员变量改变不触发任何更新

         ↓

阶段 1:父组件视角(发起者)
父组件 render()
    ↓
生成新的 Virtual DOM: <Child propA={newValue} />
    ↓
Props 流向子组件 (oldProps → newProps)
    📌 "Props Change" 是父组件 render 的结果,不是原因

         ↓

阶段 2:子组件视角(响应者)
Props Changed
    ↓
shouldComponentUpdate?
├── false → 停止更新
└── true → 子组件 render() → Diff 算法
    📌 这就是 "Props Change → SCU → Render"

         ↓

阶段 3:Commit 阶段(hostConfig)
key/type 变了?
├── 是 → 销毁重建 (removeChild + createInstance + appendChild)
└── 否 → ref 变了?
         ├── 是 → ref 重绑定 (getPublicInstance)
         └── 否 → props 变了?(deepEquals)
                  ├── 是 → 属性更新
                  │        prepareUpdate() → commitUpdate()
                  │        puerts.merge(nativePtr, props)
                  │        SynchronizeWidgetProperties()
                  └── 否 → 无操作

关键结论:
• React 组件的 Render(生成 VDOM)≠ UE Widget 的 Update(实际修改属性)
• Props Change 发生在两个 Render 之间:父组件 Render 后、子组件 Render 前
• 私有成员变量的改变永远不会触发任何更新

6. 关键技术点

puerts.merge - 高性能属性设置

优势

  • 减少 JS ↔ C++ 跨界调用次数
  • 支持嵌套对象深度合并
  • Puerts 内部优化,性能接近原生

SynchronizeProperties - 双层架构同步

为什么需要同步?

  • TypeScript 通过 puerts.merge 修改 UWidget 属性
  • Slate 层的 SWidget 不会自动更新
  • 必须调用 SynchronizeProperties() 触发同步

Slot 延迟设置

关键点

  • Slot 属性在 init() 时暂存,不立即应用
  • 父容器 AddChild() 后设置 nativeSlot,触发 setter
  • Setter 中应用 Slot 配置并同步

事件自动绑定与清理

避免内存泄漏

  • 每次绑定事件时,存储对应的清理函数
  • Widget 销毁时,自动调用所有清理函数
  • 防止 C++ Delegate 持有已销毁的 JS 函数引用

lazyloadComponents - 自定义 Widget 支持

使用场景

  • 复杂的自定义 UI 组件(在 UE 编辑器中设计)
  • 动画蓝图驱动的 Widget
  • 复用 Blueprint 团队的成果

7. 设计亮点

极简 C++ 层

统计

  • 2 个类:UReactWidget, UUMGManager
  • 5 个函数:CreateReactWidget, AddChild, RemoveChild, SynchronizeWidgetProperties, SynchronizeSlotProperties
  • 总代码量:< 150 行

零性能开销

无序列化/反序列化

  • Puerts 直接修改 C++ 对象内存,接近原生性能
  • 一次 puerts.merge 设置多个属性,减少跨界调用

完整类型安全

类型定义文件Typing/react-umg/index.d.ts(2406 行)

覆盖范围

  • ✅ 所有 UMG 组件的 Props 接口
  • ✅ 所有 Slot 类型的属性接口
  • ✅ UE 类型的 TypeScript 映射
  • ✅ 事件回调的参数类型

完全拥抱 React 生态

支持所有 React 特性

  • HooksuseState, useEffect, useContext, useRef
  • Context API:跨组件共享状态
  • Ref:访问 Widget 实例
  • Fragment<></>
  • 条件渲染{condition && <Widget />}
  • 列表渲染{items.map(item => <Widget key={item.id} />)}

自动事件管理

  • bind() 时存储清理函数
  • unbindAll() 在 Widget 销毁时自动调用
  • 防止 C++ Delegate 持有悬挂指针

8. 关键源码文件

ReactUMG 插件

C++ 层

  • Plugins/ReactUMG/Source/ReactUMG/ReactWidget.h/.cpp
  • Plugins/ReactUMG/Source/ReactUMG/UMGManager.h/.cpp

TypeScript 层

  • Plugins/ReactUMG/TypeScript/react-umg/react-umg.ts

类型定义

  • Typing/react-umg/index.d.ts(2406 行)- React-UMG 组件类型
  • Typing/ue/index.d.ts - UE5 类型映射

项目案例

  • TypeScript/src/debug-panel/DebugPanel.tsx - 调试面板(复杂布局)
  • TypeScript/src/tale-select-panel/index.tsx - 剧本选择(居中布局)
  • TypeScript/src/formation-panel/index.tsx - 编队面板(底部布局)
  • TypeScript/src/game-operation-window/ - 运营界面(完整示例)

9. UE 开发者理解指南

render() 不是 CreateWidget()

// ⚠️ UE 开发者常见误解
render() {
    return (
        <CanvasPanel>           // ← 不是每次都创建新的!
            <DragPreview />     // ← React 会检查 key 决定复用还是重建
        </CanvasPanel>
    );
}

// React 的实际行为:
// 第一次 render → 创建 CanvasPanel、创建 DragPreview
// 第二次 render → CanvasPanel 无变化则复用,DragPreview 检查 key
//   - key 相同 → 复用,只更新 props
//   - key 不同 → 销毁旧的,创建新的

React vs UE 更新模式对比

概念 UE (C++) React (TypeScript)
创建组件 CreateWidget<T>() 真的创建 <Component /> 只是描述
更新位置 Widget->SetPosition() <Component Slot={{...}} />
组件复用 手动管理,同一个指针 React 自动,key 相同则复用
强制重建 RemoveChild + CreateWidget 改变 key
每帧更新 NativeTick() setState() → render()

10. 总结

ReactUMG 通过极简的 C++ 层完整的 TypeScript 协调层,成功实现了 React 到 UMG 的无缝桥接:

  1. 三层架构清晰:开发者层(React)→ 协调层(Reconciler)→ 原生层(UMG)
  2. 核心组件职责明确:UReactWidget(容器)、UUMGManager(工具)、UEWidget(包装)、hostConfig(协调)
  3. 数据流完整:渲染、更新、事件流程清晰可追溯
  4. 关键技术精妙:puerts.merge、SynchronizeProperties、Slot 延迟、事件自动管理
  5. 设计理念先进:极简 C++、零开销、类型安全、拥抱生态

这种架构让开发者可以用 React 的方式编写 UE5 UI,同时保持原生性能完整类型安全


Version: v1.0 Last Updated: 2025-12-23