十五万字长文 - 由浅入深设计一套低代码平台(4-3)

31 阅读3分钟

低代码平台架构设计方案 - 从浅入深完整指南(4-3)

前言: 本文系统阐述了一套完整的低代码平台架构设计方案,涵盖从基础架构到企业级应用的全链路技术实现。

核心内容:

🏗️ 四层架构体系:可视化设计器、Schema协议、运行时引擎、物料体系的完整设计

🔄 全局状态管理:基于Zustand的页面级Store架构,支持跨组件数据流动

⚡ 性能优化方案:三种发布模式(纯运行时、Schema编译、混合模式)对比与实践

🎯 动作系统:枚举化业务操作设计,实现配置化与安全性的平衡

🔧 Schema编译器:深度解析编译优化策略,在保持Runtime架构一致性的同时实现70%体积优化

🚀 SSR支持:Next.js集成方案,满足SEO与首屏性能需求

📦 发布流程:从Schema保存到产物部署的完整工程化实践

适合人群:前端架构师、低代码平台开发者、对前端工程化感兴趣的技术人员

全文15万+字,涵盖架构设计、核心实现、性能优化、工程实践等多个维度,提供可直接落地的技术方案。

5.10 渲染器集成:状态注入与事件收集

关键步骤:

  1. 创建全局 Runtime (包含 GlobalStore)
  2. 遍历组件树,解析表达式注入状态
  3. 收集事件配置,转换为真实的事件处理函数
  4. 订阅 Store 变化,实现响应式更新
// renderer/Renderer.jsx
import React, { useState, useEffect, useSyncExternalStore } from 'react';
import MaterialRegistry from '../material-registry/MaterialRegistry';
import LowCodeRuntime from '../runtime/LowCodeRuntime';

/**
 * 低代码渲染器
 *
 * 职责:
 * 1. 初始化全局 Runtime (包含 GlobalStore、Methods 等)
 * 2. 递归渲染组件树
 * 3. 解析表达式 (如 ${store.users}) 并注入到 props
 * 4. 收集 events 配置并转换为事件处理函数
 * 5. 订阅 Store 变化,触发重新渲染
 */
function Renderer({ schema }) {
  // 创建全局 Runtime 实例 (整个页面只有一个)
  const [runtime] = useState(() => {
    console.log('🚀 初始化 Runtime with schema.store:', schema.store);
    return new LowCodeRuntime(schema);
  });

  // 订阅全局 Store 的变化
  // 当 Store 更新时,会触发所有使用该状态的组件重新渲染
  const store = useSyncExternalStore(
    runtime.store.subscribe.bind(runtime.store),
    () => runtime.store.getStore()
  );

  useEffect(() => {
    console.log('✅ Runtime mounted, store:', store);
    return () => {
      console.log('❌ Runtime unmounting');
      runtime.destroy();
    };
  }, []);

  /**
   * 递归渲染节点
   *
   * @param {Object} nodeSchema - 节点配置
   * @returns {ReactElement}
   */
  const renderNode = (nodeSchema) => {
    const { id, componentName, props = {}, children, events } = nodeSchema;

    // 1️⃣ 从物料注册表获取组件
    const Component = MaterialRegistry.getComponent(componentName);
    if (!Component) {
      console.warn(`❌ Component ${componentName} not found`);
      return null;
    }

    // 2️⃣ 解析 props 中的表达式,注入全局状态
    // 例如: { users: "${store.users}" }
    //   →  { users: [实际的用户数组] }
    const resolvedProps = resolvePropsWithExpressions(props, runtime);

    // 3️⃣ 收集并转换 events 配置为真实的事件处理函数
    // 例如: { onClick: { type: "callMethod", payload: {...} } }
    //   →  { onClick: async () => { runtime.callMethod(...) } }
    const eventHandlers = collectEventHandlers(id, events, runtime);

    // 4️⃣ 递归渲染子节点
    const childNodes = children?.map((child, index) =>
      renderNode({ ...child, id: child.id || `${id}_child_${index}` })
    );

    // 5️⃣ 使用 React.createElement 创建真实的 React 元素
    return React.createElement(
      Component,
      {
        key: id,
        ...resolvedProps,     // 注入状态
        ...eventHandlers      // 注入事件处理器
      },
      childNodes
    );
  };

  return (
    <div className="lowcode-renderer">
      {renderNode(schema.componentTree)}
    </div>
  );
}

/**
 * 解析 props 中的表达式
 *
 * @param {Object} props - 原始 props 配置
 * @param {LowCodeRuntime} runtime - 运行时实例
 * @returns {Object} 解析后的 props
 */
function resolvePropsWithExpressions(props, runtime) {
  const resolved = {};

  for (const [key, value] of Object.entries(props)) {
    if (typeof value === 'string' && value.includes('${')) {
      // 表达式: "${store.users}" → [实际数据]
      resolved[key] = runtime.expression.evaluate(value);

      console.log(`📊 表达式解析: ${key}:`, {
        原始值: value,
        解析结果: resolved[key]
      });
    } else if (typeof value === 'object' && value !== null) {
      // 递归解析嵌套对象
      resolved[key] = resolvePropsWithExpressions(value, runtime);
    } else {
      // 普通值直接传递
      resolved[key] = value;
    }
  }

  return resolved;
}

/**
 * 收集事件配置并转换为事件处理函数
 *
 * @param {string} nodeId - 节点ID
 * @param {Object} events - 事件配置
 * @param {LowCodeRuntime} runtime - 运行时实例
 * @returns {Object} 事件处理器映射
 */
function collectEventHandlers(nodeId, events, runtime) {
  if (!events) return {};

  const handlers = {};

  for (const [eventName, eventConfig] of Object.entries(events)) {
    // 创建事件处理函数
    // 当组件调用 props.onClick(...args) 时,会执行这个函数
    handlers[eventName] = runtime.eventSystem.createEventHandler(
      nodeId,
      eventName,
      eventConfig
    );

    console.log(`🎯 事件注册: ${eventName}`, {
      组件ID: nodeId,
      事件配置: eventConfig
    });
  }

  return handlers;
}

export default Renderer;

完整的渲染流程示意图:

Schema 定义
├── store: { users: [] }
├── methods: { handleDelete: {...} }
└── componentTree: { ... }
        ↓
┌────────────────────────────────────────────────────┐
│  1. 初始化 Runtime                                  │
│     - 创建 GlobalStore (Zustand)                   │
│     - 注册全局 Methods                              │
│     - 初始化 EventSystem、ExpressionEngine         │
└──────────────┬─────────────────────────────────────┘
               ↓
┌────────────────────────────────────────────────────┐
│  2. 递归渲染组件树                                  │
│     - 获取组件: MaterialRegistry.get('UserList')   │
│     - 解析表达式: "${store.users}" → [数组]        │
│     - 收集事件: { onDelete: {...} } → 函数          │
└──────────────┬─────────────────────────────────────┘
               ↓
┌────────────────────────────────────────────────────┐
│  3. 创建 React 元素                                 │
│     React.createElement(                           │
│       UserList,                                    │
│       {                                            │
│         users: [实际数组],  ← 来自 GlobalStore      │
│         onDelete: async (id) => {  ← 事件处理器    │
│           await runtime.callMethod('handleDelete') │
│         }                                          │
│       }                                            │
│     )                                              │
└──────────────┬─────────────────────────────────────┘
               ↓
         渲染真实的 React 组件
               ↓
         用户交互 (点击删除)
               ↓
         触发 onDelete(userId)
               ↓
         执行全局 Method
               ↓
         更新 GlobalStore
               ↓
         所有依赖该状态的组件自动重新渲染

5.11 完整案例:用户管理页面

这个案例展示了如何使用全局 Store、全局 Methods、数据源和事件系统构建一个完整的 CRUD 页面。

{
  "version": "1.0.0",

  // 全局状态 Store
  "store": {
    "users": [],
    "modalVisible": false,
    "selectedUser": null,
    "filters": {
      "keyword": "",
      "status": "all"
    }
  },

  // 数据源定义
  "dataSources": [
    {
      "id": "userList",
      "type": "http",
      "config": {
        "url": "/api/users",
        "method": "GET",
        "autoLoad": true
      }
    }
  ],

  // 页面生命周期
  "lifeCycles": {
    "onMount": [
      { "type": "setStore", "payload": { "loading": true } },
      { "type": "fetchDataSource", "payload": { "id": "userList" } }
    ]
  },

  // 全局方法
  "methods": {
    "handleDelete": {
      "params": ["userId"],
      "actions": [
        { "type": "confirm", "payload": { "message": "确认删除?" } },
        {
          "type": "request",
          "payload": {
            "url": "/api/users/${params[0]}",
            "method": "DELETE"
          }
        },
        { "type": "message", "payload": { "type": "success", "content": "删除成功" } },
        { "type": "fetchDataSource", "payload": { "id": "userList" } }
      ]
    },

    "handleEdit": {
      "params": ["user"],
      "actions": [
        {
          "type": "setStore",
          "payload": {
            "selectedUser": "${params[0]}",
            "modalVisible": true
          }
        }
      ]
    },

    "handleModalClose": {
      "actions": [
        {
          "type": "setStore",
          "payload": {
            "modalVisible": false,
            "selectedUser": null
          }
        }
      ]
    },

    "handleFilterChange": {
      "params": ["filters"],
      "actions": [
        { "type": "setStore", "payload": { "filters": "${params[0]}" } },
        { "type": "fetchDataSource", "payload": { "id": "userList" } }
      ]
    }
  },

  // 组件树
  "componentTree": {
    "componentName": "Page",
    "props": { "title": "用户管理" },
    "children": [
      // 操作栏
      {
        "componentName": "Space",
        "props": { "style": { "marginBottom": 16 } },
        "children": [
          {
            "componentName": "Button",
            "props": {
              "type": "primary",
              "children": "新增用户"
            },
            "events": {
              "onClick": {
                "type": "setStore",
                "payload": { "selectedUser": null, "modalVisible": true }
              }
            }
          },
          {
            "componentName": "Input",
            "props": {
              "placeholder": "搜索用户",
              "value": "${store.filters.keyword}"
            },
            "events": {
              "onChange": {
                "type": "callMethod",
                "payload": {
                  "method": "handleFilterChange",
                  "params": ["${Object.assign({}, store.filters, { keyword: event.target.value })}"]
                }
              }
            }
          }
        ]
      },

      // 用户表格
      {
        "componentName": "Table",
        "props": {
          "dataSource": "${dataSource.userList.data}",
          "loading": "${dataSource.userList.loading}",
          "rowKey": "id",
          "columns": [
            { "title": "用户名", "dataIndex": "username" },
            { "title": "邮箱", "dataIndex": "email" },
            { "title": "状态", "dataIndex": "status" },
            {
              "title": "操作",
              "render": {
                "componentName": "Space",
                "children": [
                  {
                    "componentName": "Button",
                    "props": {
                      "type": "link",
                      "children": "编辑"
                    },
                    "events": {
                      "onClick": {
                        "type": "callMethod",
                        "payload": {
                          "method": "handleEdit",
                          "params": ["${record}"]
                        }
                      }
                    }
                  },
                  {
                    "componentName": "Button",
                    "props": {
                      "type": "link",
                      "danger": true,
                      "children": "删除"
                    },
                    "events": {
                      "onClick": {
                        "type": "callMethod",
                        "payload": {
                          "method": "handleDelete",
                          "params": ["${record.id}"]
                        }
                      }
                    }
                  }
                ]
              }
            }
          ]
        }
      },

      // 编辑模态框
      {
        "componentName": "Modal",
        "props": {
          "visible": "${store.modalVisible}",
          "title": "${store.selectedUser ? '编辑用户' : '新增用户'}"
        },
        "events": {
          "onCancel": {
            "type": "callMethod",
            "payload": { "method": "handleModalClose" }
          }
        },
        "children": [
          {
            "componentName": "Form",
            "props": {
              "initialValues": "${store.selectedUser}"
            }
          }
        ]
      }
    ]
  }
}

数据流分析:

1. 页面加载
   ↓
   lifeCycles.onMount 触发
   ↓
   setStore({ loading: true })
   ↓
   fetchDataSource('userList')
   ↓
   请求 /api/users
   ↓
   dataSource.userList.data 更新
   ↓
   Table 组件的 dataSource="${dataSource.userList.data}" 自动更新

2. 用户点击"删除"按钮
   ↓
   触发 onClick 事件
   ↓
   callMethod('handleDelete', [userId])
   ↓
   执行 actions 序列:
     - confirm() → 用户确认
     - request(DELETE /api/users/123)
     - message('删除成功')
     - fetchDataSource('userList') → 刷新列表
   ↓
   GlobalStore 更新
   ↓
   所有依赖的组件自动重新渲染

3. 用户点击"编辑"按钮
   ↓
   callMethod('handleEdit', [userObject])
   ↓
   setStore({
     selectedUser: userObject,
     modalVisible: true
   })
   ↓
   Modal 的 visible="${store.modalVisible}" → 显示弹窗
   Form 的 initialValues="${store.selectedUser}" → 填充数据