2024年11月18日8 分钟

低代码平台实现原理:从架构设计到核心引擎

深入解析低代码平台的核心实现原理,包括可视化编辑器、Schema 驱动渲染、组件系统和物料体系

什么是低代码平台

低代码平台允许用户通过拖拽组件 + 配置属性的方式构建应用,而不是手写代码。核心理念是:把"编写代码"转化为"编辑数据(Schema)"

传统开发:需求 → 开发编写代码 → 测试 → 部署
低代码:  需求 → 拖拽 + 配置 → 实时预览 → 一键发布

整体架构

┌───────────────────────────────────────────────────────┐
│                    低代码平台架构                        │
│                                                        │
│  ┌──────────────────────────────────────────────────┐  │
│  │              可视化编辑器(IDE)                    │  │
│  │  ┌─────────┐ ┌──────────┐ ┌────────────────────┐ │  │
│  │  │ 组件面板  │ │ 画布区域  │ │ 属性配置面板        │ │  │
│  │  │         │ │          │ │                    │ │  │
│  │  │ Button  │ │ ┌──────┐ │ │ 文本: "提交"       │ │  │
│  │  │ Input   │→│ │组件实例│ │→│ 颜色: #7c3aed    │ │  │
│  │  │ Table   │ │ └──────┘ │ │ 事件: onClick     │ │  │
│  │  │ Form    │ │          │ │                    │ │  │
│  │  └─────────┘ └──────────┘ └────────────────────┘ │  │
│  └──────────────────┬───────────────────────────────┘  │
│                     │                                   │
│                     ▼  生成 / 修改                       │
│              ┌──────────────┐                           │
│              │  Page Schema  │  ← 页面的 JSON 描述       │
│              │  (JSON 数据)  │                           │
│              └──────┬───────┘                           │
│                     │                                   │
│          ┌──────────┼──────────┐                        │
│          ▼          ▼          ▼                        │
│     渲染引擎     代码生成器    存储/版本管理               │
│     (Runtime)   (CodeGen)    (DB/Git)                  │
│          │          │                                   │
│          ▼          ▼                                   │
│     运行时渲染    源码输出                                │
│     (Preview)   (Export)                               │
└───────────────────────────────────────────────────────┘

核心一:Schema 驱动

整个低代码平台的灵魂是 Schema——一份 JSON 数据,描述了页面的所有信息。

Schema 结构设计

{
  "version": "1.0",
  "pageName": "用户管理",
  "componentTree": {
    "id": "root",
    "componentName": "Page",
    "props": { "title": "用户管理系统" },
    "children": [
      {
        "id": "header-1",
        "componentName": "Header",
        "props": { "title": "用户列表" }
      },
      {
        "id": "table-1",
        "componentName": "DataTable",
        "props": {
          "columns": [
            { "title": "姓名", "dataIndex": "name" },
            { "title": "邮箱", "dataIndex": "email" }
          ],
          "dataSource": {
            "type": "api",
            "url": "/api/users",
            "method": "GET"
          }
        },
        "children": []
      },
      {
        "id": "btn-1",
        "componentName": "Button",
        "props": {
          "text": "新增用户",
          "type": "primary",
          "onClick": {
            "type": "action",
            "action": "openDialog",
            "params": { "dialogId": "dialog-1" }
          }
        }
      }
    ]
  },
  "dataSource": {
    "users": {
      "type": "fetch",
      "url": "/api/users",
      "autoFetch": true
    }
  },
  "lifeCycle": {
    "onMount": [
      { "type": "fetchData", "target": "users" }
    ]
  }
}

Schema 设计的关键决策

Schema 需要描述:
  ├── 组件树(componentTree)— 页面结构
  ├── 组件属性(props)— 外观和行为
  ├── 数据源(dataSource)— 数据从哪来
  ├── 事件/动作(events/actions)— 交互逻辑
  ├── 生命周期(lifeCycle)— 页面级钩子
  └── 状态(state)— 页面内部状态

核心二:渲染引擎

渲染引擎负责把 Schema 转换成真实的 React 组件树

递归渲染器

// 核心渲染器:递归遍历 Schema,渲染组件
function SchemaRenderer({ schema }: { schema: ComponentSchema }) {
  // 1. 从组件注册表中查找组件
  const Component = ComponentRegistry.get(schema.componentName)

  if (!Component) {
    return <div>未知组件: {schema.componentName}</div>
  }

  // 2. 处理 props(可能包含表达式、数据绑定)
  const resolvedProps = resolveProps(schema.props)

  // 3. 递归渲染子组件
  const children = schema.children?.map(child => (
    <SchemaRenderer key={child.id} schema={child} />
  ))

  // 4. 渲染组件
  return <Component {...resolvedProps}>{children}</Component>
}

// 使用
function PageRenderer({ pageSchema }) {
  return <SchemaRenderer schema={pageSchema.componentTree} />
}

组件注册表

// 组件注册表:维护所有可用组件
class ComponentRegistry {
  private static components = new Map<string, React.ComponentType>()

  static register(name: string, component: React.ComponentType) {
    this.components.set(name, component)
  }

  static get(name: string) {
    return this.components.get(name)
  }
}

// 注册内置组件
ComponentRegistry.register('Button', AntdButton)
ComponentRegistry.register('Input', AntdInput)
ComponentRegistry.register('DataTable', CustomDataTable)
ComponentRegistry.register('Form', AntdForm)

// 注册自定义组件(扩展性)
ComponentRegistry.register('BizUserCard', UserCard)

属性解析器(处理动态表达式)

function resolveProps(props: Record<string, any>): Record<string, any> {
  const resolved: Record<string, any> = {}

  for (const [key, value] of Object.entries(props)) {
    if (typeof value === 'string' && value.startsWith('{{') && value.endsWith('}}')) {
      // 表达式解析:{{ state.userName }} → 实际值
      const expression = value.slice(2, -2).trim()
      resolved[key] = evaluateExpression(expression)
    } else if (typeof value === 'object' && value?.type === 'action') {
      // 事件绑定:{ type: 'action', action: 'openDialog' } → 函数
      resolved[key] = createActionHandler(value)
    } else {
      resolved[key] = value
    }
  }

  return resolved
}

核心三:可视化编辑器

拖拽系统

// 基于 HTML5 Drag & Drop API 或 dnd-kit
function DraggableComponent({ componentMeta }) {
  const handleDragStart = (e: DragEvent) => {
    e.dataTransfer.setData('componentName', componentMeta.name)
    e.dataTransfer.setData('defaultProps', JSON.stringify(componentMeta.defaultProps))
  }

  return (
    <div draggable onDragStart={handleDragStart}>
      {componentMeta.icon}
      <span>{componentMeta.title}</span>
    </div>
  )
}

function Canvas({ schema, onDrop }) {
  const handleDrop = (e: DragEvent) => {
    const componentName = e.dataTransfer.getData('componentName')
    const defaultProps = JSON.parse(e.dataTransfer.getData('defaultProps'))

    // 计算放置位置(在哪个父组件的哪个位置)
    const dropTarget = calculateDropTarget(e)

    // 更新 Schema:在目标位置插入新组件
    const newNode = {
      id: generateId(),
      componentName,
      props: defaultProps,
      children: [],
    }
    onDrop(dropTarget, newNode)
  }

  return (
    <div onDrop={handleDrop} onDragOver={e => e.preventDefault()}>
      <SchemaRenderer schema={schema} />
    </div>
  )
}

选中和高亮

// 选中组件时显示蓝色边框和操作按钮
function SelectionOverlay({ selectedId, schema }) {
  const element = document.querySelector(`[data-component-id="${selectedId}"]`)
  if (!element) return null

  const rect = element.getBoundingClientRect()

  return (
    <div style={{
      position: 'fixed',
      top: rect.top,
      left: rect.left,
      width: rect.width,
      height: rect.height,
      border: '2px solid #7c3aed',
      pointerEvents: 'none',
    }}>
      {/* 组件名标签 */}
      <span style={{ position: 'absolute', top: -24 }}>
        {schema.componentName}
      </span>
      {/* 操作按钮 */}
      <div style={{ position: 'absolute', top: -24, right: 0 }}>
        <button onClick={() => deleteComponent(selectedId)}>删除</button>
        <button onClick={() => copyComponent(selectedId)}>复制</button>
      </div>
    </div>
  )
}

核心四:事件和动作系统

// 动作注册表
const ActionRegistry = {
  // 页面跳转
  navigateTo: ({ url }) => { window.location.href = url },

  // 打开弹窗
  openDialog: ({ dialogId }) => { setDialogVisible(dialogId, true) },

  // 发送请求
  fetchData: async ({ url, method, body }) => {
    const res = await fetch(url, { method, body: JSON.stringify(body) })
    return res.json()
  },

  // 设置状态
  setState: ({ key, value }) => { pageState.set(key, value) },

  // 显示提示
  showMessage: ({ type, content }) => { message[type](content) },

  // 组合动作(串行执行多个动作)
  sequence: async ({ actions }) => {
    for (const action of actions) {
      await ActionRegistry[action.type](action.params)
    }
  },
}

核心五:出码引擎(可选)

将 Schema 转换成真实的源代码,让项目可以脱离平台运行:

// Schema → React 源码
function generateCode(schema: ComponentSchema): string {
  const imports = new Set<string>()
  const code = generateComponentCode(schema, imports)

  return `
import React from 'react'
${[...imports].map(i => `import { ${i} } from 'antd'`).join('\n')}

export default function GeneratedPage() {
  return (
    ${code}
  )
}
`
}

function generateComponentCode(schema, imports) {
  imports.add(schema.componentName)
  const propsStr = Object.entries(schema.props)
    .map(([k, v]) => `${k}={${JSON.stringify(v)}}`)
    .join(' ')

  const childrenStr = schema.children
    ?.map(child => generateComponentCode(child, imports))
    .join('\n')

  return `<${schema.componentName} ${propsStr}>${childrenStr || ''}</${schema.componentName}>`
}

业界方案对比

平台技术方案特点
阿里 LowCodeEngineSchema + 插件化开源,可扩展
腾讯 TDesign Starter模板 + 配置企业级模板
百度 AmisJSON 配置纯 JSON 驱动,零代码
RetoolSQL + 组件内部工具专用
AppsmithWidget + API开源,API 集成强

总结

低代码平台的核心技术栈:

  1. Schema 设计:定义一套 JSON 协议描述页面
  2. 渲染引擎:递归解析 Schema → 组件树
  3. 可视化编辑器:拖拽 + 属性面板 → 修改 Schema
  4. 组件体系:注册表 + 物料市场
  5. 事件系统:动作编排 + 表达式引擎
  6. 出码引擎:Schema → 可维护的源代码