低代码平台架构设计方案 - 从浅入深完整指南(4-2)
前言: 本文系统阐述了一套完整的低代码平台架构设计方案,涵盖从基础架构到企业级应用的全链路技术实现。
核心内容:
🏗️ 四层架构体系:可视化设计器、Schema协议、运行时引擎、物料体系的完整设计
🔄 全局状态管理:基于Zustand的页面级Store架构,支持跨组件数据流动
⚡ 性能优化方案:三种发布模式(纯运行时、Schema编译、混合模式)对比与实践
🎯 动作系统:枚举化业务操作设计,实现配置化与安全性的平衡
🔧 Schema编译器:深度解析编译优化策略,在保持Runtime架构一致性的同时实现70%体积优化
🚀 SSR支持:Next.js集成方案,满足SEO与首屏性能需求
📦 发布流程:从Schema保存到产物部署的完整工程化实践
适合人群:前端架构师、低代码平台开发者、对前端工程化感兴趣的技术人员
全文15万+字,涵盖架构设计、核心实现、性能优化、工程实践等多个维度,提供可直接落地的技术方案。
5.7.1 全局方法 (Global Methods) 的实现原理
重要澄清: methods 不是字符串形式的函数体,而是 JSON 配置的动作序列!
常见误解:
❌ 错误理解: methods 是字符串形式的代码
{
"methods": {
"handleDelete": "async function(userId) { await fetch(...) }" // ❌ 错误!
}
}
✅ 正确理解: methods 是动作序列的配置
{
"methods": {
"handleDelete": {
"params": ["userId"],
"actions": [ // ← JSON 配置,不是代码字符串
{ "type": "confirm", ... },
{ "type": "request", ... }
]
}
}
}
实现原理:
┌─────────────────────────────────────────────────┐
│ 用户配置全局方法 (Schema) │
├─────────────────────────────────────────────────┤
│ { │
│ "methods": { │
│ "handleDelete": { │
│ "params": ["userId"], │
│ "actions": [ │
│ { "type": "confirm", ... }, │
│ { "type": "request", ... }, │
│ { "type": "message", ... } │
│ ] │
│ } │
│ } │
│ } │
└────────────────┬────────────────────────────────┘
│ 运行时注册
↓
┌─────────────────────────────────────────────────┐
│ Runtime 初始化时,将 methods 注册到 Map │
├─────────────────────────────────────────────────┤
│ this.methods = new Map() │
│ this.methods.set('handleDelete', { │
│ params: ['userId'], │
│ actions: [...] │
│ }) │
└────────────────┬────────────────────────────────┘
│ 组件事件触发
↓
┌─────────────────────────────────────────────────┐
│ 用户点击删除按钮 │
├─────────────────────────────────────────────────┤
│ <Button │
│ onClick={{ │
│ type: "callMethod", │
│ payload: { │
│ method: "handleDelete", │
│ params: [123] ← 传入 userId │
│ } │
│ }} │
│ /> │
└────────────────┬────────────────────────────────┘
│ 执行动作
↓
┌─────────────────────────────────────────────────┐
│ 运行时执行 │
├─────────────────────────────────────────────────┤
│ // callMethod 动作被触发 │
│ async callMethod(methodName, params) { │
│ // 1. 从 Map 中获取方法配置 │
│ const methodConfig = this.methods.get( │
│ 'handleDelete' │
│ ); │
│ │
│ // 2. 依次执行 actions 序列 │
│ for (const action of methodConfig.actions) { │
│ await actionRegistry.execute( │
│ action.type, │
│ action.payload │
│ ); │
│ } │
│ } │
└─────────────────────────────────────────────────┘
完整示例:
{
"store": {
"users": []
},
"methods": {
// ❌ 不是这样: "handleDelete": "function() { ... }"
// ✅ 而是这样: JSON 配置的动作序列
"handleDelete": {
"params": ["userId"], // 方法参数
"actions": [ // 动作序列(按顺序执行)
// 动作1: 弹确认框
{
"type": "confirm",
"payload": { "message": "确认删除用户?" }
},
// 动作2: 发送删除请求
{
"type": "request",
"payload": {
"url": "/api/users/${params[0]}", // 使用参数
"method": "DELETE"
}
},
// 动作3: 显示提示
{
"type": "message",
"payload": {
"type": "success",
"content": "删除成功"
}
},
// 动作4: 更新列表
{
"type": "setStore",
"payload": {
"users": "${store.users.filter(u => u.id !== params[0])}"
}
}
]
}
},
"componentTree": {
"children": [
{
"componentName": "Button",
"props": { "children": "删除" },
"events": {
"onClick": {
"type": "callMethod", // 触发 callMethod 动作
"payload": {
"method": "handleDelete", // 方法名
"params": [123] // 传入参数
}
}
}
}
]
}
}
运行时实现:
class LowCodeRuntime {
constructor(schema) {
// 注册全局方法
this.methods = new Map();
if (schema.methods) {
Object.entries(schema.methods).forEach(([name, config]) => {
this.methods.set(name, config);
});
}
}
/**
* 调用全局方法
* @param {string} methodName - 方法名
* @param {Array} params - 参数数组
*/
async callMethod(methodName, params = []) {
const methodConfig = this.methods.get(methodName);
if (!methodConfig) {
throw new Error(`Method "${methodName}" not found`);
}
console.log(`🎯 调用方法: ${methodName}`, params);
// 构建上下文(包含参数)
const context = {
params,
store: this.store.getStore(),
dataSource: this.dataSource.getContext()
};
// 依次执行 actions 序列
const results = [];
for (const action of methodConfig.actions) {
// 解析 payload 中的表达式(如 ${params[0]})
const resolvedPayload = this.expression.evaluateObject(
action.payload,
context
);
// 执行动作
const result = await this.actionRegistry.execute(
action.type,
resolvedPayload
);
results.push(result);
// 如果是 confirm 返回 false,中断执行
if (action.type === 'confirm' && !result) {
console.log('❌ 用户取消操作');
break;
}
}
return results;
}
}
关键点总结:
| 维度 | 传统开发 | 低代码平台 |
|---|---|---|
| 方法定义 | JavaScript 函数 | JSON 配置的动作序列 |
| 执行方式 | 直接调用函数 | 运行时解析并执行动作 |
| 代码形式 | function() { ... } | { params: [], actions: [...] } |
| 灵活性 | 高(可以写任何代码) | 中(受限于预定义动作) |
| 安全性 | 低(任意代码执行) | 高(只能使用注册的动作) |
| 学习成本 | 需要会编程 | 配置即可,无需编程 |
为什么不用字符串形式的代码?
- ❌ 安全风险:
eval()或new Function()可以执行任意代码,存在 XSS 风险 - ❌ 难以解析: 字符串代码难以静态分析和验证
- ❌ 不可移植: JavaScript 代码无法跨平台(如小程序)
- ✅ JSON 配置: 安全、可序列化、可验证、易于理解
- ✅ 动作系统: 提供足够的灵活性,同时保证安全
5.8 表达式引擎
支持的表达式类型:
// 1. 全局Store访问
"${store.userName}"
"${store.user.profile.email}"
// 2. 数据源访问
"${dataSource.userList.data}"
"${dataSource.userList.loading}"
// 3. 计算表达式
"${store.price * store.quantity}"
"${store.users.length > 0}"
// 4. 函数调用
"${store.users.filter(u => u.age > 18)}"
"${store.name.toUpperCase()}"
// 5. 三元表达式
"${store.isVip ? 'VIP用户' : '普通用户'}"
// 6. 模板字符串
"`用户: ${store.userName}, 年龄: ${store.age}`"
// 7. 混合使用
"${store.users.find(u => u.id === dataSource.currentUserId.data)}"
表达式引擎实现:
// runtime/ExpressionEngine.js
class ExpressionEngine {
constructor(runtime) {
this.runtime = runtime;
this.cache = new Map(); // 表达式缓存
}
/**
* 评估表达式
*
* 表达式上下文说明:
* - store: 全局 Store (来自 schema.store)
* - dataSource: 数据源状态 (来自 runtime.dataSource)
* - 不提供 state,避免与 store 混淆
*/
evaluate(expression, extraContext = {}) {
if (typeof expression !== 'string' || !expression.includes('${')) {
return expression;
}
// 检查缓存
let compiledFn = this.cache.get(expression);
if (!compiledFn) {
compiledFn = this.compile(expression);
this.cache.set(expression, compiledFn);
}
// 构建表达式上下文
const context = {
store: this.runtime.store.getStore(), // 全局 Store
dataSource: this.getDataSourceContext(), // 数据源
...extraContext // 额外的上下文(如事件参数)
};
try {
return compiledFn(context);
} catch (error) {
console.error('Expression evaluation error:', error);
console.error('Expression:', expression);
console.error('Context:', context);
return undefined;
}
}
// 编译表达式为函数
compile(expression) {
const code = expression.slice(2, -1); // 去除 ${ }
// 使用 with 语句创建上下文作用域
// 允许直接访问 store.xxx 而不是 context.store.xxx
return new Function('context', `
with(context) {
return ${code};
}
`);
}
// 获取数据源上下文
getDataSourceContext() {
const context = {};
this.runtime.dataSource.dataSources.forEach((ds, id) => {
context[id] = {
data: ds.data,
loading: ds.loading,
error: ds.error
};
});
return context;
}
/**
* 批量解析对象中的所有表达式
* 用于解析组件的 props
*/
evaluateObject(obj, extraContext = {}) {
if (!obj || typeof obj !== 'object') {
return obj;
}
const result = Array.isArray(obj) ? [] : {};
for (const [key, value] of Object.entries(obj)) {
if (typeof value === 'string') {
result[key] = this.evaluate(value, extraContext);
} else if (typeof value === 'object' && value !== null) {
result[key] = this.evaluateObject(value, extraContext);
} else {
result[key] = value;
}
}
return result;
}
}
/**
* 使用示例:
*
* const engine = new ExpressionEngine(runtime);
*
* // 简单访问
* engine.evaluate("${store.userName}"); // → "John"
*
* // 计算
* engine.evaluate("${store.users.length}"); // → 5
*
* // 复杂表达式
* engine.evaluate("${store.users.filter(u => u.active).map(u => u.name)}");
* // → ["Alice", "Bob"]
*
* // 批量解析
* engine.evaluateObject({
* users: "${store.users}",
* count: "${store.users.length}",
* title: "用户列表"
* });
* // → { users: [...], count: 5, title: "用户列表" }
*/
5.9 完整运行时实现
// runtime/LowCodeRuntime.js
/**
* 低代码运行时 - 整个页面的核心引擎
*
* 架构说明:
* - 每个页面有一个 Runtime 实例 (页面级单例)
* - Runtime 包含全局 Store、全局 Methods、事件系统等
* - 所有组件通过 Runtime 访问数据和触发动作
*/
class LowCodeRuntime {
constructor(schema) {
this.schema = schema;
console.log('🚀 初始化 LowCodeRuntime');
// 初始化全局状态管理器
this.store = new GlobalStoreManager(schema.store || {});
console.log('✅ GlobalStore 初始化完成:', schema.store);
// 初始化事件总线 (用于兄弟组件通信)
this.eventBus = new EventBus();
// 初始化表达式引擎 (解析 ${store.xxx})
this.expression = new ExpressionEngine(this);
// 初始化动作注册表 (内置动作 + 自定义动作)
this.actionRegistry = new ActionRegistry(this);
// 初始化事件系统 (处理组件事件)
this.eventSystem = new EventSystem(this);
// 初始化数据源管理器
this.dataSource = new DataSourceManager(this);
// 注册全局方法
this.methods = new Map();
if (schema.methods) {
Object.entries(schema.methods).forEach(([name, config]) => {
this.methods.set(name, config);
console.log(`✅ 注册全局方法: ${name}`);
});
}
// 异步初始化
this.init();
}
/**
* 初始化运行时
*/
async init() {
try {
// 注册数据源
if (this.schema.dataSources) {
this.schema.dataSources.forEach(ds => {
this.dataSource.register(ds);
console.log(`✅ 注册数据源: ${ds.id}`);
});
}
// 执行 onMount 生命周期
if (this.schema.lifeCycles?.onMount) {
console.log('🔄 执行 onMount 生命周期');
await this.eventSystem.executeActionSequence(
this.schema.lifeCycles.onMount,
{}
);
}
console.log('✅ Runtime 初始化完成');
} catch (error) {
console.error('❌ Runtime 初始化失败:', error);
}
}
/**
* 调用全局方法
*/
async callMethod(methodName, params = []) {
const methodConfig = this.methods.get(methodName);
if (!methodConfig) {
throw new Error(`Method "${methodName}" not found`);
}
console.log(`🎯 调用全局方法: ${methodName}`, params);
// 执行方法的 actions 序列
return await this.eventSystem.executeActionSequence(
methodConfig.actions,
{ params }
);
}
/**
* 销毁运行时
*/
destroy() {
try {
// 执行 onUnmount 生命周期
if (this.schema.lifeCycles?.onUnmount) {
console.log('🔄 执行 onUnmount 生命周期');
this.eventSystem.executeActionSequence(
this.schema.lifeCycles.onUnmount,
{}
);
}
// 清理事件总线
this.eventBus.clear();
console.log('✅ Runtime 销毁完成');
} catch (error) {
console.error('❌ Runtime 销毁失败:', error);
}
}
}
/**
* 使用示例:
*
* const schema = {
* store: { users: [], loading: false },
* methods: { handleDelete: { actions: [...] } },
* dataSources: [{ id: 'userApi', ... }],
* componentTree: { ... }
* };
*
* const runtime = new LowCodeRuntime(schema);
*
* // 访问全局 Store
* const users = runtime.store.getStore('users');
* runtime.store.setStore({ loading: true });
*
* // 调用全局方法
* await runtime.callMethod('handleDelete', [userId]);
*
* // 解析表达式
* const value = runtime.expression.evaluate('${store.users.length}');
*/