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

33 阅读3分钟

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

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

核心内容:

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

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

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

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

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

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

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

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

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

七、物料生态体系

7.1 物料分层

┌────────────────────────────────────────┐
│         页面模板 (Page Templates)        │  完整页面
├────────────────────────────────────────┤
│          区块 (Blocks)                  │  组合型组件
├────────────────────────────────────────┤
│       业务组件 (Business Components)    │  业务封装
├────────────────────────────────────────┤
│       基础组件 (Base Components)        │  原子组件
└────────────────────────────────────────┘

示例:

// 1. 基础组件
@lowcode-materials/button
@lowcode-materials/input
@lowcode-materials/table

// 2. 业务组件
@mycompany/user-selector     // 用户选择器
@mycompany/dept-tree         // 部门树

// 3. 区块
@mycompany/login-form-block  // 登录表单区块
@mycompany/user-list-block   // 用户列表区块

// 4. 页面模板
@mycompany/dashboard-template // 仪表盘模板
@mycompany/form-page-template // 表单页模板

7.2 物料标准规范

// 物料包结构规范
{
  "name": "@scope/material-name",
  "version": "1.0.0",
  "description": "物料描述",

  // 入口文件
  "main": "dist/index.js",
  "module": "dist/index.es.js",

  // 依赖
  "peerDependencies": {
    "react": ">=16.8.0",
    "react-dom": ">=16.8.0"
  },

  // 低代码元信息
  "lowcode": {
    "meta": "dist/metadata.json",
    "preview": "dist/preview.png"
  },

  // 其他标准字段
  "keywords": ["lowcode", "material", "component"],
  "license": "MIT"
}

7.3 物料市场

// 物料市场后端API
class MaterialMarketAPI {
  // 获取物料列表
  async list(params) {
    // GET /api/materials?category=basic&keyword=button
    return {
      total: 100,
      items: [
        {
          id: 'mat_001',
          name: '@lc/button',
          title: '按钮',
          description: '基础按钮组件',
          category: 'basic',
          version: '1.0.0',
          downloads: 1000,
          rating: 4.5,
          author: 'lowcode-team',
          preview: 'https://cdn.../preview.png'
        }
      ]
    };
  }

  // 获取物料详情
  async detail(materialId) {
    // GET /api/materials/:id
    return {
      id: 'mat_001',
      name: '@lc/button',
      versions: ['1.0.0', '1.1.0', '2.0.0'],
      readme: '# Button Component\n...',
      metadata: { /* lowcode-meta.json */ },
      dependencies: ['react'],
      demos: [
        { title: '基础用法', code: '...' }
      ]
    };
  }

  // 安装物料
  async install(materialName, version) {
    // POST /api/materials/install
    // 后端执行: npm install @lc/button@1.0.0
    return { success: true };
  }
}

八、性能与安全

8.1 渲染性能优化

// 1. 组件缓存
const MaterialCache = new Map();

function getCachedComponent(componentName) {
  if (!MaterialCache.has(componentName)) {
    const Component = loadComponent(componentName);
    MaterialCache.set(componentName, React.memo(Component));
  }
  return MaterialCache.get(componentName);
}

// 2. 表达式缓存
class ExpressionCache {
  constructor(maxSize = 1000) {
    this.cache = new Map();
    this.maxSize = maxSize;
  }

  get(key) {
    return this.cache.get(key);
  }

  set(key, value) {
    if (this.cache.size >= this.maxSize) {
      const firstKey = this.cache.keys().next().value;
      this.cache.delete(firstKey);
    }
    this.cache.set(key, value);
  }
}

// 3. 虚拟列表
import { FixedSizeList } from 'react-window';

function LargeList({ items }) {
  return (
    <FixedSizeList
      height={600}
      itemCount={items.length}
      itemSize={50}
      width="100%"
    >
      {({ index, style }) => (
        <div style={style}>{items[index]}</div>
      )}
    </FixedSizeList>
  );
}

8.2 安全防护

// 1. 表达式沙箱
class SafeExpressionEvaluator {
  constructor() {
    this.allowedGlobals = ['Math', 'Date', 'JSON', 'Object', 'Array'];
    this.blockedGlobals = ['window', 'document', 'eval', 'Function'];
  }

  evaluate(expression, context) {
    // 检查是否包含危险代码
    if (this.containsDangerousCode(expression)) {
      throw new Error('Expression contains dangerous code');
    }

    // 创建安全上下文
    const safeContext = this.createSafeContext(context);

    // 使用Function构造器(限制作用域)
    try {
      const fn = new Function(
        ...Object.keys(safeContext),
        `return (${expression});`
      );
      return fn(...Object.values(safeContext));
    } catch (error) {
      console.error('Expression evaluation error:', error);
      return null;
    }
  }

  containsDangerousCode(expression) {
    const dangerousPatterns = [
      /eval\s*\(/,
      /Function\s*\(/,
      /\bwindow\b/,
      /\bdocument\b/,
      /__proto__/,
      /constructor/
    ];

    return dangerousPatterns.some(pattern => pattern.test(expression));
  }

  createSafeContext(context) {
    // 只暴露安全的API
    return {
      ...context,
      Math,
      Date,
      JSON: JSON,
      console: {
        log: console.log.bind(console),
        warn: console.warn.bind(console)
      }
    };
  }
}

// 2. XSS防护
import DOMPurify from 'dompurify';

function sanitizeProps(props) {
  const sanitized = {};

  for (const [key, value] of Object.entries(props)) {
    if (typeof value === 'string') {
      // 对字符串内容进行清洗
      sanitized[key] = DOMPurify.sanitize(value);
    } else if (key === 'dangerouslySetInnerHTML') {
      // 禁止使用dangerouslySetInnerHTML
      console.warn('dangerouslySetInnerHTML is not allowed');
      continue;
    } else {
      sanitized[key] = value;
    }
  }

  return sanitized;
}

// 3. 权限控制
class PermissionManager {
  checkPermission(user, action, resource) {
    // 检查用户是否有权限执行某个操作
    const userRoles = user.roles || [];
    const requiredRoles = this.getRequiredRoles(action, resource);

    return requiredRoles.some(role => userRoles.includes(role));
  }

  getRequiredRoles(action, resource) {
    const permissionMap = {
      'schema.edit': ['admin', 'editor'],
      'schema.publish': ['admin'],
      'material.install': ['admin', 'developer']
    };

    const key = `${resource}.${action}`;
    return permissionMap[key] || [];
  }
}