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

22 阅读7分钟

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

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

核心内容:

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

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

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

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

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

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

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

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

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

五、运行时能力设计

核心能力: 事件处理、状态管理、数据流、组件通信 解决问题: 复杂页面逻辑、数据交互、业务流程编排

5.1 运行时核心模块

┌─────────────────────────────────────────────────────────────┐
│                     Low-Code Runtime                          │
├─────────────────────────────────────────────────────────────┤
│                                                               │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐      │
│  │  Event       │  │   State      │  │  DataSource  │      │
│  │  System      │  │   Manager    │  │  Manager     │      │
│  └──────────────┘  └──────────────┘  └──────────────┘      │
│         │                  │                  │              │
│         └──────────────────┼──────────────────┘              │
│                            ↓                                 │
│                   ┌─────────────────┐                        │
│                   │  Expression     │                        │
│                   │  Engine         │                        │
│                   └─────────────────┘                        │
│                            │                                 │
│                            ↓                                 │
│                   ┌─────────────────┐                        │
│                   │  Action         │                        │
│                   │  Executor       │                        │
│                   └─────────────────┘                        │
│                                                               │
└─────────────────────────────────────────────────────────────┘

5.2 Schema扩展协议

在基础Schema基础上,扩展运行时能力:

{
  "version": "1.0.0",
  "componentTree": { /* 组件树 */ },

  // 新增:全局状态Store (基于Zustand实现)
  // ⚠️ 注意: 这是全局状态,不是某个组件的useState
  // 所有组件都可以通过表达式 ${store.xxx} 访问
  "store": {
    "userInfo": null,
    "loading": false,
    "tableData": []
  },

  // 新增:数据源定义
  "dataSources": [
    {
      "id": "userApi",
      "type": "http",
      "config": {
        "url": "/api/users",
        "method": "GET"
      }
    }
  ],

  // 新增:生命周期钩子
  "lifeCycles": {
    "onMount": [
      { "type": "setStore", "payload": { "loading": true } },
      { "type": "fetchDataSource", "payload": { "id": "userApi" } }
    ]
  },

  // 新增:全局方法定义
  // 所有组件的事件都可以通过 callMethod 调用这些方法
  "methods": {
    "handleDelete": {
      "params": ["id"],
      "actions": [
        { "type": "confirm", "payload": { "message": "确认删除?" } },
        { "type": "request", "payload": { "url": "/api/users/${id}", "method": "DELETE" } }
      ]
    }
  }
}

架构说明:

┌─────────────────────────────────────────────────────────┐
│  LowCodeRuntime (页面级单例)                              │
│  ┌───────────────────────────────────────────────────┐  │
│  │  GlobalStore (Zustand)                            │  │
│  │  - 所有状态集中管理                                 │  │
│  │  - 任意组件可通过表达式访问                          │  │
│  │  - 类似 Redux Store,不是组件useState                │  │
│  └───────────────────────────────────────────────────┘  │
│                                                          │
│  ┌───────────────────────────────────────────────────┐  │
│  │  GlobalMethods (全局方法表)                        │  │
│  │  - 定义在顶层,所有组件可调用                        │  │
│  │  - 通过 callMethod 动作触发                         │  │
│  └───────────────────────────────────────────────────┘  │
└─────────────────┬────────────────────────────────────────┘
                  │
        所有组件通过表达式/事件访问
                  │
     ┌────────────┼────────────┐
     ↓            ↓            ↓
┌─────────┐  ┌─────────┐  ┌─────────┐
│Component│  │Component│  │Component│
│    A    │  │    B    │  │    C    │
└─────────┘  └─────────┘  └─────────┘

5.3 事件系统

事件绑定方式:

{
  "componentName": "Button",
  "events": {
    // 方式1: 简单动作
    "onClick": {
      "type": "setState",
      "payload": { "loading": true }
    },

    // 方式2: 动作序列
    "onClick": [
      { "type": "validateForm", "payload": { "formId": "userForm" } },
      { "type": "request", "payload": { "url": "/api/submit" } },
      { "type": "navigate", "payload": { "url": "/success" } }
    ],

    // 方式3: 调用全局方法
    "onClick": {
      "type": "callMethod",
      "payload": { "method": "handleSubmit" }
    }
  }
}

事件系统实现:

// runtime/EventSystem.js
class EventSystem {
  constructor(runtime) {
    this.runtime = runtime;
  }

  // 创建事件处理器
  createEventHandler(nodeId, eventName, eventConfig) {
    return async (event, ...args) => {
      try {
        // 执行事件处理
        const result = await this.executeEvent(eventConfig, {
          event,
          args,
          nodeId
        });
        return result;
      } catch (error) {
        console.error(`Event execution failed:`, error);
      }
    };
  }

  // 执行事件(支持单个动作或动作序列)
  async executeEvent(eventConfig, context) {
    if (eventConfig.type) {
      return await this.executeAction(eventConfig, context);
    }

    if (Array.isArray(eventConfig)) {
      return await this.executeActionSequence(eventConfig, context);
    }
  }

  // 执行单个动作
  async executeAction(action, context) {
    const executor = this.runtime.actionRegistry.get(action.type);
    const payload = this.resolvePayload(action.payload, context);
    return await executor.execute(payload, context);
  }

  // 执行动作序列
  async executeActionSequence(actions, context) {
    const results = [];
    for (const action of actions) {
      // 支持条件执行
      if (action.condition) {
        const shouldExecute = this.runtime.expression.evaluate(
          action.condition,
          context
        );
        if (!shouldExecute) continue;
      }

      const result = await this.executeAction(action, context);
      results.push(result);

      // 支持错误中断
      if (result.error && action.stopOnError) {
        break;
      }
    }
    return results;
  }
}

5.4 全局状态管理 (Global Store)

核心概念: 使用 Zustand 实现全局状态管理,类似于 Redux,但配置更简单。

⚠️ 重要: 这不是组件级的 useState,而是页面级的全局 Store!

// runtime/GlobalStoreManager.js
import { createStore } from 'zustand';
import { immer } from 'zustand/middleware/immer';

/**
 * 全局状态管理器
 *
 * 架构对比:
 * - 传统React: 组件内 useState() → 组件私有状态
 * - 低代码平台: GlobalStore → 所有组件共享的全局状态
 *
 * 类似于:
 * - Redux Store (但更轻量)
 * - Zustand Store
 * - MobX Store
 */
class GlobalStoreManager {
  constructor(initialStore = {}) {
    // 创建全局 Store
    this.store = createStore(
      immer((set, get) => ({
        ...initialStore,  // 来自 Schema 的 store 字段

        setStore: (updater) => {
          set((store) => {
            if (typeof updater === 'function') {
              updater(store);
            } else {
              Object.assign(store, updater);
            }
          });
        },

        getStore: () => get()
      }))
    );
  }

  // 获取状态(支持路径访问)
  getStore(path) {
    const store = this.store.getState();
    if (!path) return store;

    // 支持 "user.name" 路径访问
    return path.split('.').reduce((obj, key) => obj?.[key], store);
  }

  // 设置状态
  setStore(updates) {
    this.store.getState().setStore(updates);
  }

  // 订阅状态变化 (用于响应式更新)
  subscribe(listener) {
    return this.store.subscribe(listener);
  }
}

/**
 * 使用示例:
 *
 * // Schema 定义
 * {
 *   "store": { "users": [], "loading": false }
 * }
 *
 * // 运行时
 * const storeManager = new GlobalStoreManager({ users: [], loading: false });
 *
 * // 任意组件可访问
 * const users = storeManager.getStore('users');
 * storeManager.setStore({ loading: true });
 */

5.5 数据源管理

支持HTTP/GraphQL等多种数据源:

// runtime/DataSourceManager.js
class DataSourceManager {
  constructor(runtime) {
    this.runtime = runtime;
    this.dataSources = new Map();
  }

  // 注册数据源
  register(dataSourceConfig) {
    const { id, type, config } = dataSourceConfig;
    this.dataSources.set(id, {
      id,
      type,
      config,
      loading: false,
      data: null,
      error: null
    });

    // 自动加载
    if (config.autoLoad) {
      this.load(id);
    }
  }

  // 加载数据
  async load(id, params = {}) {
    const dataSource = this.dataSources.get(id);
    if (!dataSource) throw new Error(`DataSource "${id}" not found`);

    dataSource.loading = true;
    this.updateState(id, { loading: true, error: null });

    try {
      const data = await this.executeLoad(dataSource, params);

      dataSource.data = data;
      dataSource.loading = false;

      this.updateState(id, { data, loading: false });
      return data;
    } catch (error) {
      dataSource.error = error;
      this.updateState(id, { loading: false, error: error.message });
      throw error;
    }
  }

  // 执行加载
  async executeLoad(dataSource, params) {
    const { type, config } = dataSource;

    switch (type) {
      case 'http':
        const { url, method = 'GET', headers = {} } = config;
        const response = await fetch(this.resolveUrl(url, params), {
          method,
          headers: { 'Content-Type': 'application/json', ...headers },
          body: method !== 'GET' ? JSON.stringify(params.data) : undefined
        });
        return await response.json();

      case 'graphql':
        // GraphQL实现
        break;

      case 'static':
        return config.data;
    }
  }

  // 解析URL变量: /api/users/${id}
  resolveUrl(url, params) {
    return url.replace(/\${(\w+)}/g, (match, key) => {
      return params[key] ?? this.runtime.state.getState(key) ?? match;
    });
  }

  // 更新到全局状态
  updateState(id, updates) {
    this.runtime.state.setState({
      [`dataSource_${id}`]: updates
    });
  }
}

5.6 组件与全局Store的交互

⚠️ 概念澄清: 这里不是传统意义的"父子组件通信"!

在低代码平台中,所有组件都是通过全局Store进行数据交互,而不是通过组件层级的 props 传递。

架构对比:

// 传统React: 父子组件通信
function ParentPage() {
  const [users, setUsers] = useState([]);  // 父组件的状态

  return <UserList users={users} />;  // 通过props传递
}

// 低代码平台: 组件与全局Store交互
const schema = {
  "store": { "users": [] },  // 全局Store (不属于任何组件)
  "componentTree": {
    "children": [
      {
        "componentName": "UserList",
        "props": {
          "users": "${store.users}"  // 从全局Store读取
        }
      }
    ]
  }
};

1. 组件读取全局状态

{
  "componentName": "UserList",
  "props": {
    // 通过表达式从全局Store读取数据
    "users": "${store.users}",
    "loading": "${store.loading}",

    // 支持计算属性
    "userCount": "${store.users.length}",

    // 支持复杂表达式
    "activeUsers": "${store.users.filter(u => u.active)}"
  }
}

2. 组件触发全局方法

{
  "componentName": "UserList",
  "props": {
    "users": "${store.users}",

    // onDelete 不是传统的回调函数
    // 而是一个事件配置,会调用全局methods中的handleDelete
    "onDelete": {
      "type": "callMethod",
      "payload": { "method": "handleDelete" }
    }
  }
}

数据流示意图:

┌─────────────────────────────────────────────────────┐
│  全局 Runtime                                        │
│  ┌─────────────────────────────────────────────┐   │
│  │ GlobalStore: { users: [...], loading: false }│   │
│  └─────────────────────────────────────────────┘   │
│  ┌─────────────────────────────────────────────┐   │
│  │ Methods: { handleDelete: {...} }            │   │
│  └─────────────────────────────────────────────┘   │
└────────────┬────────────────────────────────────────┘
             │
    所有组件通过表达式访问
             │
  ┌──────────┼──────────┐
  ↓          ↓          ↓
┌─────┐  ┌─────┐  ┌─────────┐
│Button│  │Table│  │UserList │
└─────┘  └─────┘  └─────────┘
   │                    │
   │ onClick            │ onDelete
   │                    │
   └────────┬───────────┘
            ↓
     触发全局 handleDelete 方法
            ↓
     更新 GlobalStore.users
            ↓
     所有依赖 store.users 的组件自动重新渲染

2. 兄弟组件通信(EventBus)

// runtime/EventBus.js
class EventBus {
  constructor() {
    this.events = new Map();
  }

  // 订阅事件
  on(eventName, callback) {
    if (!this.events.has(eventName)) {
      this.events.set(eventName, []);
    }
    this.events.get(eventName).push(callback);

    return () => this.off(eventName, callback);
  }

  // 触发事件
  async emit(eventName, data) {
    const listeners = this.events.get(eventName) || [];
    const results = [];

    for (const listener of listeners) {
      const result = await listener(data);
      results.push(result);
    }

    return results;
  }
}

Schema中的通信配置:

{
  "componentTree": {
    "children": [
      // 组件A: 发送消息
      {
        "componentName": "Button",
        "events": {
          "onClick": {
            "type": "emitEvent",
            "payload": {
              "eventName": "refreshList",
              "data": { "timestamp": "${Date.now()}" }
            }
          }
        }
      },

      // 组件B: 接收消息
      {
        "componentName": "Table",
        "listeners": {
          "refreshList": [
            { "type": "fetchDataSource", "payload": { "id": "tableData" } }
          ]
        }
      }
    ]
  }
}

5.7 动作系统 (Action System)

核心问题: 低代码平台中,用户不能直接写 JavaScript 代码,如何实现业务逻辑(如删除、请求接口、显示提示)?

解决方案: 提供预定义的"动作积木",用户通过 JSON 配置来组合使用。

架构设计理念:

传统开发:
  用户点击删除 → 写 JS 函数 → 调用 API → 刷新列表
  ❌ 需要编程能力

低代码平台:
  用户点击删除 → 配置动作序列 → 运行时执行 → 自动完成
  ✅ 无需编程,配置即可

动作系统的三层架构:

┌─────────────────────────────────────────────────┐
│  用户配置层 (JSON Schema)                        │
│  {                                              │
│    "events": {                                  │
│      "onClick": [                               │
│        { "type": "confirm", "payload": {...} }, │
│        { "type": "request", "payload": {...} }, │
│        { "type": "message", "payload": {...} }  │
│      ]                                          │
│    }                                            │
│  }                                              │
└────────────────┬────────────────────────────────┘
                 │ 运行时解析
                 ↓
┌─────────────────────────────────────────────────┐
│  动作注册表 (ActionRegistry)                     │
│  - confirm: 确认对话框                           │
│  - request: HTTP 请求                            │
│  - message: 消息提示                             │
│  - setStore: 更新状态                            │
│  - ...                                          │
└────────────────┬────────────────────────────────┘
                 │ 执行引擎
                 ↓
┌─────────────────────────────────────────────────┐
│  原生 JavaScript 实现                            │
│  async execute(payload) {                       │
│    await fetch(url, { method, body })           │
│    return response.json()                       │
│  }                                              │
└─────────────────────────────────────────────────┘

内置15+常用动作:

每个动作都是一个预定义的 JavaScript 函数,用户通过 JSON 配置来调用。

// runtime/ActionRegistry.js
class ActionRegistry {
  constructor(runtime) {
    this.runtime = runtime;
    this.actions = new Map();
    this.registerBuiltinActions();
  }

  /**
   * 注册动作
   * @param {string} type - 动作类型(如 'confirm', 'request')
   * @param {Object} handler - 动作处理器
   * @param {Function} handler.execute - 实际执行的函数
   */
  register(type, handler) {
    this.actions.set(type, handler);
  }

  /**
   * 执行动作
   * @param {string} type - 动作类型
   * @param {Object} payload - 动作参数
   */
  async execute(type, payload) {
    const action = this.actions.get(type);
    if (!action) {
      throw new Error(`Action "${type}" not found`);
    }
    return await action.execute(payload);
  }

  registerBuiltinActions() {
    // 1. 状态管理
    this.register('setStore', {
      execute: async (payload) => {
        this.runtime.store.setStore(payload);
      }
    });

    // 2. HTTP 请求
    this.register('request', {
      execute: async ({ url, method = 'GET', data, headers = {} }) => {
        console.log(`📡 HTTP ${method} ${url}`);
        const response = await fetch(url, {
          method,
          headers: { 'Content-Type': 'application/json', ...headers },
          body: data ? JSON.stringify(data) : undefined
        });
        const result = await response.json();
        console.log(`✅ Response:`, result);
        return result;
      }
    });

    // 3. 数据源加载
    this.register('fetchDataSource', {
      execute: async ({ id, params }) => {
        return await this.runtime.dataSource.load(id, params);
      }
    });

    // 4. 页面导航
    this.register('navigate', {
      execute: async ({ url, replace = false }) => {
        replace ? window.location.replace(url) : window.location.href = url;
      }
    });

    // 5. 消息提示
    this.register('message', {
      execute: async ({ type = 'info', content }) => {
        // 集成 Ant Design Message 或其他 UI 库
        window.antdMessage?.[type](content);
      }
    });

    // 6. 确认对话框
    this.register('confirm', {
      execute: async ({ message, title }) => {
        // 返回 true/false,可以用于条件执行
        return window.confirm(message);
      }
    });

    // 7-8. 弹窗管理
    this.register('openModal', {
      execute: async ({ modalId }) => {
        this.runtime.store.setStore({ [`${modalId}_visible`]: true });
      }
    });

    this.register('closeModal', {
      execute: async ({ modalId }) => {
        this.runtime.store.setStore({ [`${modalId}_visible`]: false });
      }
    });

    // 9-10. 表单操作
    this.register('validateForm', {
      execute: async ({ formId }) => {
        // 触发表单验证
        const form = this.runtime.formRegistry.get(formId);
        return await form.validate();
      }
    });

    this.register('resetForm', {
      execute: async ({ formId }) => {
        const form = this.runtime.formRegistry.get(formId);
        form.resetFields();
      }
    });

    // 11. 触发事件(用于组件间通信)
    this.register('emitEvent', {
      execute: async ({ eventName, data }) => {
        await this.runtime.eventBus.emit(eventName, data);
      }
    });

    // 12. 调用全局方法
    this.register('callMethod', {
      execute: async ({ method, params = [] }) => {
        return await this.runtime.callMethod(method, params);
      }
    });

    // 13. 延迟执行
    this.register('delay', {
      execute: async ({ milliseconds }) => {
        await new Promise(resolve => setTimeout(resolve, milliseconds));
      }
    });

    // 14. 条件执行
    this.register('condition', {
      execute: async ({ condition, thenActions, elseActions }) => {
        const shouldExecute = this.runtime.expression.evaluate(condition);
        const actions = shouldExecute ? thenActions : elseActions;
        if (actions) {
          return await this.runtime.eventSystem.executeActionSequence(actions);
        }
      }
    });

    // 15. 循环执行
    this.register('loop', {
      execute: async ({ items, actions }) => {
        const results = [];
        for (const item of items) {
          const result = await this.runtime.eventSystem.executeActionSequence(
            actions,
            { item }
          );
          results.push(result);
        }
        return results;
      }
    });
  }
}

为什么需要动作系统?

  1. 降低门槛: 用户不需要会编程,通过配置即可实现业务逻辑
  2. 标准化: 统一的动作接口,所有操作都是 { type, payload } 的形式
  3. 可组合: 多个动作可以组成序列,实现复杂逻辑
  4. 可扩展: 开发者可以注册自定义动作
  5. 安全可控: 动作在沙箱中执行,避免任意代码执行的风险

使用示例:

// 用户配置(不需要写代码)
{
  "events": {
    "onClick": [
      // 步骤1: 弹出确认框
      {
        "type": "confirm",
        "payload": { "message": "确认删除?" }
      },
      // 步骤2: 发送 HTTP 请求
      {
        "type": "request",
        "payload": {
          "url": "/api/users/123",
          "method": "DELETE"
        }
      },
      // 步骤3: 显示成功提示
      {
        "type": "message",
        "payload": {
          "type": "success",
          "content": "删除成功"
        }
      },
      // 步骤4: 刷新列表
      {
        "type": "fetchDataSource",
        "payload": { "id": "userList" }
      }
    ]
  }
}
// 运行时自动执行(用户看不到这部分代码)
async function handleClick() {
  // 执行动作序列
  const confirmed = await actionRegistry.execute('confirm', {
    message: "确认删除?"
  });

  if (confirmed) {
    await actionRegistry.execute('request', {
      url: "/api/users/123",
      method: "DELETE"
    });

    await actionRegistry.execute('message', {
      type: "success",
      content: "删除成功"
    });

    await actionRegistry.execute('fetchDataSource', {
      id: "userList"
    });
  }
}

自定义动作:

开发者可以注册自定义动作来扩展平台能力:

// 注册自定义上传动作
runtime.actionRegistry.register('customUpload', {
  execute: async ({ file, onProgress, onSuccess }) => {
    const formData = new FormData();
    formData.append('file', file);

    const xhr = new XMLHttpRequest();
    xhr.upload.onprogress = (e) => {
      const percent = (e.loaded / e.total) * 100;
      runtime.eventSystem.executeEvent(onProgress, { percent });
    };

    xhr.onload = () => {
      const result = JSON.parse(xhr.responseText);
      runtime.eventSystem.executeEvent(onSuccess, { result });
    };

    xhr.open('POST', '/api/upload');
    xhr.send(formData);
  }
});

// 用户就可以在 Schema 中使用
{
  "events": {
    "onChange": {
      "type": "customUpload",
      "payload": {
        "file": "${event.target.files[0]}"
      }
    }
  }
}