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?.());
}
});
这种设计提供了高度可扩展的低代码渲染架构,支持复杂的业务场景,同时保持良好的性能和开发体验。
挣钱养家

浙公网安备 33010602011771号