一、回顾和目标
在过去几个迭代中,我逐步实现了 Runtime 的各项能力:
arduino
Runtime 能力
├── Context + Expression → 数据容器 + 表达式引擎
├── 调度系统 → ctx.set → notifyUpward → rerender
├── Schema Runtime → 逻辑解析(condition / loop)
├── Lifecycle → onMount / onUnmount 生命周期,执行时机
├── DataSource → 声明式对接外部 API,数据请求能力
└── Events → 用户交互绑定
我打算在一个 Schema 中让这些能力协同工作,完成一个"可交互的列表页"的闭环验证。
二、验证清单
我设计的这个"可交互的列表页"覆盖了以下 7 项 Runtime 能力:
vbnet
页面加载(onMount)→ 组件挂载时触发初始化逻辑
数据请求(DataSource)→ 声明式加载外部接口数据
loading 状态(Expression)→ 动态数据绑定到 UI
列表渲染(loop) → 数组展开为多个节点
条件控制(condition) → 根据状态控制节点显隐
用户点击(event)→ 用户交互触发方法执行
UI 更新(调度)→ 状态变更驱动视图更新
如果这 7 项能在一个 Demo 中同时工作,说明 Runtime 最小版形成了完整闭环。
三、案例案例:可交互的列表页
Demo Schema 设计如下:
typescript
export const schema: Schema = {
id: 'page',
componentName: 'div',
state: { postList: [], loading: false },
methods: {
loadList: {
type: 'JSFunction',
value: `async function(ctx) {
ctx.set('state.loading', true);
const data = await fetch('https://jsonplaceholder.typicode.com/posts?_limit=5')
.then(r => r.json());
ctx.set('state.postList', data);
ctx.set('state.loading', false);
}`,
},
},
lifeCycles: {
onMount: {
type: 'JSFunction',
value: `function(ctx) { ctx.methods.loadList(ctx) }`,
},
},
dataSource: {
list: [
{
id: 'authorList',
type: 'fetch',
options: { uri: 'https://jsonplaceholder.typicode.com/users?_limit=3', method: 'GET' },
isInit: true,
},
],
},
children: [
{
id: 'author-title',
componentName: 'p',
props: { children: '作者(DataSource 自动加载):' },
},
{
id: 'author-item',
componentName: 'span',
loop: 'data.authorList',
loopArgs: ['author'],
props: { children: '{{author.name}} | ' },
},
{
id: 'refresh-btn',
componentName: 'button',
props: { children: '刷新文章' },
events: { onClick: 'loadList' },
},
{
id: 'loading-text',
componentName: 'p',
condition: 'state.loading',
props: { children: '加载中...' },
},
{
id: 'postList-item',
componentName: 'p',
loop: 'state.postList',
loopArgs: ['post'],
props: { children: '{{post.title}}' },
},
],
}
整个页面分为两个区域------作者区 (DataSource 驱动)和文章区(onMount + Events 驱动),分别验证两种不同的数据获取方式。
四、每项能力在 Schema 中的映射
| 能力 | Schema 片段 | 详细设计 |
|---|---|---|
| DataSource | dataSource: { list: [{ id: 'authorList', isInit: true, ... }] } |
低代码接入外部数据能力:DataSource Runtime 的设计 |
| Lifecycle | lifeCycles: { onMount: { type: 'JSFunction', value: '...' } } |
低代码 Runtime Lifecycle:逻辑执行时机 |
| Events | events: { onClick: 'loadList' } |
低代码页面如何响应用户交互:Events Runtime 的设计 |
| condition | condition: 'state.loading' |
低代码的逻辑解释能力:Schema Runtime 的设计 |
| loop | loop: 'state.postList', loopArgs: ['post'] |
低代码的逻辑解释能力:Schema Runtime 的设计 |
| Expression | props: { children: '{{post.title}}' } |
低代码 Runtime 策略注入:让表达式引擎真正可扩展 |
| 调度系统 | ctx.set('state.loading', true) → rerender |
低代码 Runtime 的调度系统:"数据变化 → UI更新"的最小架构 |
五、执行流程全景
页面从加载到用户交互的完整链路:
kotlin
=== 页面加载 ===
1. ReactRenderer.render(schema)
→ RendererRoot 挂载
→ PreviewRuntime.createContext 创建 rootCtx(编译 methods、缓存 lifeCycles)
2. RendererNodeItem useEffect 触发:
├── runLifecycle('onMount')
│ → fn(ctx) → ctx.methods.loadList(ctx)
│ → ctx.set('state.loading', true) → rerender → "加载中..." 出现
│ → fetch → ctx.set('state.postList', data) → rerender → 文章列表渲染
│ → ctx.set('state.loading', false) → rerender → "加载中..." 消失
│
└── runDataSource(ctx, schema)
→ isInit=true → fetch users
→ ctx.set('data.authorList', data) → rerender → 作者列表渲染
=== 用户点击"刷新文章" ===
3. onClick 触发 resolveEvents 包装的回调
→ rootCtx.methods.loadList(rootCtx, event)
→ 重复 Step 2 的 loadList 流程
→ loading 显隐 + 文章列表更新
两条数据加载路径独立运行、互不干扰:
- DataSource 路径 :
isInit→ 自动 fetch →data.authorList→ loop 渲染 - onMount/Events 路径 :method 内手动 fetch →
state.postList→ loop 渲染
两者最终都通过 ctx.set → notifyUpward → rerender 驱动 UI 更新,共享同一套调度机制。
六、总结
回顾整个 Runtime 系统的能力演进:
kotlin
能力 作用 核心机制
─────────────────────────────────────────────────────
Renderer 能显示 Schema → React Element
Expression 能计算 {{state.xxx}} → 值
调度系统 能刷新 ctx.set → notifyUpward → rerender
Lifecycle 能调度 onMount/onUnmount 时机触发
DataSource 能接入数据 声明式配置 → 自动请求 → ctx.data
Events 能交互 事件声明 → 方法查找 → 回调执行
这些能力的完成,已经构成了 Runtime 的 MVP 版图。即一段 JSON 能驱动一个可交互、有数据、有状态的页面 ------ 这就是 Runtime 层的价值:让 Schema 从"描述"变成"执行"。