Vue 低代码平台渲染引擎设计

Vue 低代码平台渲染引擎设计

1. 核心架构设计

1.1 整体架构

// 渲染引擎核心接口定义
interface RenderEngine {
  schema: PageSchema;           // 页面Schema
  components: ComponentMap;     // 组件映射
  dataSource: DataSource;       // 数据源
  events: EventSystem;          // 事件系统
  render(): VNode;              // 渲染方法
}

// 页面Schema结构
interface PageSchema {
  id: string;
  name: string;
  components: ComponentSchema[];
  dataSource: DataSourceConfig;
  events: EventConfig[];
  style: PageStyle;
}

// 组件Schema
interface ComponentSchema {
  id: string;
  type: string;                // 组件类型
  componentName: string;       // 组件名称
  props: Record<string, any>;  // 组件属性
  events?: ComponentEvent[];   // 组件事件
  children?: ComponentSchema[]; // 子组件
  style?: CSSProperties;       // 样式
  dataBinding?: DataBinding;   // 数据绑定
}

2. Schema 设计

2.1 页面 Schema 定义

{
  "page": {
    "id": "page_001",
    "name": "用户管理页面",
    "layout": "flex",
    "style": {
      "padding": "20px",
      "backgroundColor": "#f5f5f5"
    },
    "dataSource": {
      "userList": {
        "type": "api",
        "url": "/api/users",
        "method": "GET",
        "autoLoad": true
      }
    },
    "components": [
      {
        "id": "search_form",
        "type": "container",
        "componentName": "ElForm",
        "props": {
          "inline": true,
          "model": "{{searchForm}}"
        },
        "children": [
          {
            "id": "search_input",
            "type": "input",
            "componentName": "ElInput",
            "props": {
              "placeholder": "请输入用户名",
              "modelValue": "{{searchForm.keyword}}"
            },
            "events": [
              {
                "name": "change",
                "action": "SEARCH_USER"
              }
            ]
          }
        ]
      }
    ]
  }
}

2.2 组件 Schema 扩展

// 详细的组件Schema定义
interface ComponentSchema {
  id: string;
  type: ComponentType;
  componentName: string;
  props: {
    // 静态属性
    [key: string]: any;
    // 动态属性绑定
    modelValue?: string; // {{data.field}}
    // 样式属性
    class?: string;
    style?: CSSProperties;
  };
  events?: Array<{
    name: string;        // 事件名称
    action: string;      // 动作类型
    payload?: any;       // 动作参数
    handler?: string;    // 自定义处理函数
  }>;
  children?: ComponentSchema[];
  // 数据绑定配置
  dataBinding?: {
    type: 'api' | 'local' | 'computed';
    source: string;
    field?: string;
    transform?: string; // 数据转换函数
  };
  // 条件渲染
  conditions?: Condition[];
  // 循环渲染
  loop?: {
    dataSource: string;
    itemName: string;
    indexName?: string;
  };
  // 插槽配置
  slots?: {
    [slotName: string]: ComponentSchema[];
  };
}

// 条件渲染配置
interface Condition {
  expression: string;    // JS表达式
  operator: '==' | '!=' | '>' | '<' | 'includes' | string;
  value: any;
}

3. 渲染引擎实现

3.1 核心渲染器

<template>
  <div class="low-code-renderer">
    <template v-for="component in normalizedComponents" :key="component.id">
      <component-renderer
        :schema="component"
        :data="pageData"
        @action="handleAction"
      />
    </template>
  </div>
</template>

<script setup lang="ts">
import { ref, computed, onMounted } from 'vue';
import ComponentRenderer from './ComponentRenderer.vue';

// Props
interface Props {
  schema: PageSchema;
  components: ComponentMap;
}
const props = defineProps<Props>();

// 页面数据
const pageData = ref<Record<string, any>>({});

// 标准化组件数据
const normalizedComponents = computed(() => {
  return processComponents(props.schema.components, pageData.value);
});

// 处理组件(处理循环、条件等)
function processComponents(components: ComponentSchema[], data: any) {
  return components.flatMap(component => {
    // 条件渲染检查
    if (component.conditions && !checkConditions(component.conditions, data)) {
      return [];
    }

    // 循环渲染处理
    if (component.loop) {
      const list = getValueByPath(data, component.loop.dataSource) || [];
      return list.map((item: any, index: number) => ({
        ...component,
        id: `${component.id}_${index}`,
        props: processProps(component.props, { 
          ...data, 
          [component.loop.itemName]: item,
          [component.loop.indexName || 'index']: index 
        })
      }));
    }

    // 普通组件
    return [{
      ...component,
      props: processProps(component.props, data)
    }];
  });
}

// 处理属性中的动态绑定
function processProps(props: Record<string, any>, data: any) {
  const processed: Record<string, any> = {};
  
  Object.keys(props).forEach(key => {
    const value = props[key];
    
    if (typeof value === 'string' && value.startsWith('{{') && value.endsWith('}}')) {
      // 处理动态绑定 {{data.field}}
      const expression = value.slice(2, -2).trim();
      processed[key] = getValueByPath(data, expression);
    } else {
      processed[key] = value;
    }
  });
  
  return processed;
}

// 动作处理
function handleAction(action: string, payload: any) {
  switch (action) {
    case 'SEARCH_USER':
      handleSearch(payload);
      break;
    case 'SUBMIT_FORM':
      handleSubmit(payload);
      break;
    // 其他动作...
  }
}

// 初始化数据
onMounted(async () => {
  await loadDataSources();
});

// 加载数据源
async function loadDataSources() {
  if (props.schema.dataSource) {
    for (const [key, config] of Object.entries(props.schema.dataSource)) {
      if (config.autoLoad) {
        pageData.value[key] = await fetchDataSource(config);
      }
    }
  }
}
</script>

3.2 组件渲染器

<template>
  <component
    :is="resolveComponent(schema.componentName)"
    v-bind="mergedProps"
    v-on="eventHandlers"
  >
    <!-- 渲染子组件 -->
    <template v-if="schema.children && schema.children.length">
      <component-renderer
        v-for="child in normalizedChildren"
        :key="child.id"
        :schema="child"
        :data="data"
        @action="$emit('action', $event)"
      />
    </template>
    
    <!-- 渲染插槽 -->
    <template v-for="(slotComponents, slotName) in schema.slots" :key="slotName">
      <template #[slotName]>
        <component-renderer
          v-for="component in slotComponents"
          :key="component.id"
          :schema="component"
          :data="data"
          @action="$emit('action', $event)"
        />
      </template>
    </template>
  </component>
</template>

<script setup lang="ts">
import { computed } from 'vue';

interface Props {
  schema: ComponentSchema;
  data: Record<string, any>;
}

const props = defineProps<Props>();
const emit = defineEmits<{
  action: [action: string, payload: any];
}>();

// 组件映射
const componentMap = {
  // 布局组件
  'ElRow': () => import('element-plus').then(mod => mod.ElRow),
  'ElCol': () => import('element-plus').then(mod => mod.ElCol),
  'ElForm': () => import('element-plus').then(mod => mod.ElForm),
  // 表单组件
  'ElInput': () => import('element-plus').then(mod => mod.ElInput),
  'ElButton': () => import('element-plus').then(mod => mod.ElButton),
  'ElTable': () => import('element-plus').then(mod => mod.ElTable),
  // 自定义业务组件
  'UserAvatar': () => import('@/components/UserAvatar.vue'),
  // ...
};

// 解析组件
async function resolveComponent(name: string) {
  const resolver = componentMap[name];
  if (!resolver) {
    console.warn(`Component ${name} not found`);
    return null;
  }
  return await resolver();
}

// 处理后的属性
const mergedProps = computed(() => {
  const baseProps = { ...props.schema.props };
  
  // 处理样式
  if (props.schema.style) {
    baseProps.style = props.schema.style;
  }
  
  return baseProps;
});

// 事件处理器
const eventHandlers = computed(() => {
  const handlers: Record<string, Function> = {};
  
  props.schema.events?.forEach(event => {
    handlers[event.name] = (...args: any[]) => {
      emit('action', event.action, {
        ...event.payload,
        $event: args[0],
        $data: props.data
      });
    };
  });
  
  return handlers;
});

// 处理子组件
const normalizedChildren = computed(() => {
  return processComponents(props.schema.children || [], props.data);
});
</script>

4. 数据源管理

4.1 数据源管理器

class DataSourceManager {
  private data: Map<string, any> = new Map();
  private subscribers: Map<string, Function[]> = new Map();

  // 注册数据源
  async registerDataSource(config: DataSourceConfig) {
    const { key, type, options } = config;
    
    switch (type) {
      case 'api':
        return await this.setupApiDataSource(key, options);
      case 'local':
        return this.setupLocalDataSource(key, options);
      case 'computed':
        return this.setupComputedDataSource(key, options);
    }
  }

  // API数据源
  private async setupApiDataSource(key: string, options: ApiDataSourceOptions) {
    const { url, method = 'GET', params, autoLoad = true } = options;
    
    if (autoLoad) {
      try {
        const response = await this.fetchData(url, method, params);
        this.setData(key, response);
      } catch (error) {
        console.error(`Failed to load data source ${key}:`, error);
      }
    }
    
    // 返回数据获取函数
    return {
      reload: () => this.fetchData(url, method, params).then(data => {
        this.setData(key, data);
        return data;
      })
    };
  }

  // 设置数据
  setData(key: string, value: any) {
    this.data.set(key, value);
    this.notifySubscribers(key, value);
  }

  // 获取数据
  getData(key: string) {
    return this.data.get(key);
  }

  // 订阅数据变化
  subscribe(key: string, callback: Function) {
    if (!this.subscribers.has(key)) {
      this.subscribers.set(key, []);
    }
    this.subscribers.get(key)!.push(callback);
    
    // 返回取消订阅函数
    return () => {
      const callbacks = this.subscribers.get(key) || [];
      const index = callbacks.indexOf(callback);
      if (index > -1) {
        callbacks.splice(index, 1);
      }
    };
  }

  private notifySubscribers(key: string, value: any) {
    const callbacks = this.subscribers.get(key) || [];
    callbacks.forEach(callback => callback(value));
  }
}

5. 事件系统

5.1 动作处理器

class ActionHandler {
  private dataManager: DataSourceManager;
  private router: Router;

  constructor(deps: { dataManager: DataSourceManager; router: Router }) {
    this.dataManager = deps.dataManager;
    this.router = deps.router;
  }

  async handle(action: string, payload: any) {
    const [actionType, ...rest] = action.split('.');
    
    switch (actionType) {
      case 'NAVIGATE':
        return this.handleNavigate(payload);
      case 'API':
        return this.handleApiCall(payload);
      case 'DATA':
        return this.handleDataOperation(payload);
      case 'MODAL':
        return this.handleModal(payload);
      default:
        console.warn(`Unknown action: ${action}`);
    }
  }

  // 路由跳转
  private handleNavigate(payload: any) {
    const { path, query, params } = payload;
    this.router.push({ path, query, params });
  }

  // API调用
  private async handleApiCall(payload: any) {
    const { key, url, method, data, onSuccess, onError } = payload;
    
    try {
      const response = await fetch(url, {
        method,
        headers: { 'Content-Type': 'application/json' },
        body: data ? JSON.stringify(data) : undefined
      });
      
      const result = await response.json();
      
      if (onSuccess) {
        await this.handle(onSuccess, { $response: result });
      }
      
      return result;
    } catch (error) {
      if (onError) {
        await this.handle(onError, { $error: error });
      }
      throw error;
    }
  }

  // 数据操作
  private handleDataOperation(payload: any) {
    const { operation, key, value } = payload;
    
    switch (operation) {
      case 'SET':
        this.dataManager.setData(key, value);
        break;
      case 'UPDATE':
        const current = this.dataManager.getData(key);
        this.dataManager.setData(key, { ...current, ...value });
        break;
      case 'DELETE':
        this.dataManager.setData(key, undefined);
        break;
    }
  }
}

6. 动态组件注册

6.1 组件库管理

class ComponentLibrary {
  private components: Map<string, ComponentDefinition> = new Map();
  private meta: Map<string, ComponentMeta> = new Map();

  // 注册组件
  registerComponent(name: string, definition: ComponentDefinition) {
    this.components.set(name, definition);
  }

  // 注册组件元数据
  registerComponentMeta(name: string, meta: ComponentMeta) {
    this.meta.set(name, meta);
  }

  // 获取组件
  getComponent(name: string): ComponentDefinition | undefined {
    return this.components.get(name);
  }

  // 获取组件元数据
  getComponentMeta(name: string): ComponentMeta | undefined {
    return this.meta.get(name);
  }

  // 获取所有组件
  getAllComponents(): ComponentMeta[] {
    return Array.from(this.meta.values());
  }
}

// 组件元数据定义
interface ComponentMeta {
  name: string;
  title: string;
  category: string; // 分类:form、layout、business等
  props: PropMeta[];
  events?: EventMeta[];
  slots?: SlotMeta[];
}

interface PropMeta {
  name: string;
  type: 'string' | 'number' | 'boolean' | 'array' | 'object';
  default?: any;
  required?: boolean;
  label: string;
  component?: string; // 用于属性编辑的组件
  options?: any[]; // 可选值
}

// 初始化组件库
export const componentLibrary = new ComponentLibrary();

// 注册Element Plus组件
componentLibrary.registerComponentMeta('ElInput', {
  name: 'ElInput',
  title: '输入框',
  category: 'form',
  props: [
    {
      name: 'modelValue',
      type: 'string',
      label: '值',
      required: true
    },
    {
      name: 'placeholder',
      type: 'string',
      label: '占位符'
    },
    {
      name: 'disabled',
      type: 'boolean',
      label: '禁用',
      default: false
    }
  ],
  events: [
    {
      name: 'change',
      label: '值改变'
    },
    {
      name: 'input',
      label: '输入'
    }
  ]
});

7. 渲染优化

7.1 组件缓存和优化

// 组件缓存管理器
class ComponentCache {
  private cache = new Map<string, any>();
  private staticComponents = new Set<string>();

  // 预编译静态组件
  preCompile(schema: PageSchema) {
    schema.components.forEach(component => {
      if (this.isStaticComponent(component)) {
        this.compileComponent(component);
      }
    });
  }

  // 判断是否为静态组件
  private isStaticComponent(component: ComponentSchema): boolean {
    return !component.dataBinding && 
           !component.conditions && 
           !component.loop &&
           !hasDynamicProps(component.props);
  }

  // 编译组件
  private compileComponent(component: ComponentSchema) {
    const key = this.generateKey(component);
    // 这里可以预编译为渲染函数
    this.cache.set(key, this.createRenderFunction(component));
  }

  // 获取缓存的组件
  get(key: string) {
    return this.cache.get(key);
  }
}

// 虚拟滚动优化(针对长列表)
const VirtualListRenderer = defineComponent({
  props: {
    components: Array as PropType<ComponentSchema[]>,
    data: Object
  },
  setup(props) {
    const containerRef = ref<HTMLElement>();
    const visibleRange = ref({ start: 0, end: 50 });
    
    // 监听滚动,计算可见区域
    const handleScroll = useThrottleFn(() => {
      if (!containerRef.value) return;
      
      const scrollTop = containerRef.value.scrollTop;
      const clientHeight = containerRef.value.clientHeight;
      
      const start = Math.floor(scrollTop / 50);
      const end = start + Math.ceil(clientHeight / 50) + 5; // 缓冲5个
      
      visibleRange.value = {
        start: Math.max(0, start),
        end: Math.min(props.components.length, end)
      };
    }, 16);
    
    const visibleComponents = computed(() => {
      const { start, end } = visibleRange.value;
      return props.components.slice(start, end);
    });
    
    return {
      containerRef,
      visibleComponents,
      handleScroll
    };
  },
  template: `
    <div ref="containerRef" class="virtual-container" @scroll="handleScroll">
      <div class="virtual-content" :style="{ height: totalHeight + 'px' }">
        <div
          v-for="component in visibleComponents"
          :key="component.id"
          class="virtual-item"
          :style="{ transform: `translateY(${component.index * 50}px)` }"
        >
          <component-renderer
            :schema="component"
            :data="data"
          />
        </div>
      </div>
    </div>
  `
});

8. 设计器集成

8.1 设计器与渲染器通信

// 设计器上下文
interface DesignerContext {
  selectedComponent: string | null;
  hoveredComponent: string | null;
  isDesignMode: boolean;
  updateComponent: (id: string, updates: Partial<ComponentSchema>) => void;
  deleteComponent: (id: string) => void;
  addComponent: (parentId: string, component: ComponentSchema) => void;
}

// 提供设计器上下文
export const useDesigner = () => {
  return inject<DesignerContext>('designer');
};

// 可编辑组件包装器
const EditableWrapper = defineComponent({
  props: {
    schema: Object as PropType<ComponentSchema>,
    data: Object
  },
  setup(props, { slots }) {
    const designer = useDesigner();
    const isSelected = computed(() => 
      designer?.selectedComponent === props.schema.id
    );
    
    const handleClick = (e: MouseEvent) => {
      e.stopPropagation();
      designer?.selectComponent(props.schema.id);
    };
    
    return () => h('div', {
      class: [
        'editable-component',
        { 'selected': isSelected.value }
      ],
      onClick: handleClick
    }, slots.default?.());
  }
});

这种设计提供了高度可扩展的低代码渲染架构,支持复杂的业务场景,同时保持良好的性能和开发体验。

posted @ 2025-10-14 12:27  阿木隆1237  阅读(56)  评论(0)    收藏  举报