VTJ:NodeModel 抽象机制

5 阅读7分钟

NodeModel 抽象机制

引言

本文件系统性阐述 NodeModel 抽象机制在低代码平台中的设计与实现,重点覆盖以下方面:

  • NodeModel 的设计理念与抽象层次,以及其在项目模型体系中的定位
  • DOM 节点在低代码平台中的表示方法与数据结构
  • NodeModel 与 BlockModel 的关系、分工与协作
  • 节点层级关系管理(父子、兄弟)的维护机制
  • 属性管理、事件处理、指令与状态同步的核心能力
  • NodeModel 的继承与扩展机制,以及如何基于它开发自定义节点类型
  • 完整 API 参考与生命周期使用示例
  • NodeModel 在渲染引擎中的作用及与虚拟 DOM 的对应关系

项目结构

NodeModel 位于核心包中,围绕“节点”这一基础抽象,配套实现了属性、事件、指令等子模型,并通过 BlockModel 将多个 NodeModel 组织为可渲染的区块。渲染侧由设计器框架负责将 DSL 渲染为真实应用。

graph TB
subgraph "核心模型"
NM["NodeModel<br/>节点模型"]
PM["PropModel<br/>属性模型"]
EM["EventModel<br/>事件模型"]
DM["DirectiveModel<br/>指令模型"]
BM["BlockModel<br/>区块模型"]
end
subgraph "协议与工具"
SH["shared.ts<br/>类型与表达式定义"]
TO["tools/emitter.ts<br/>事件总线"]
end
subgraph "渲染框架"
RD["Renderer<br/>渲染器"]
end
NM --> PM
NM --> EM
NM --> DM
BM --> NM
RD --> BM
RD --> NM
SH --> NM
SH --> BM
TO --> NM
TO --> BM

核心组件

  • NodeModel:节点抽象,承载节点 ID、名称、来源、可见性、锁定、插槽、子节点、属性、事件、指令等;提供更新、增删改查、层级移动、可见性与锁定传播、销毁等能力。
  • BlockModel:区块抽象,聚合多个 NodeModel,提供区块级状态、生命周期、方法、计算属性、watch、CSS、props、emits、slots、inject、数据源等管理,并支持节点的添加、删除、移动与克隆。
  • PropModel:属性模型,封装属性值与默认值,支持 toDsl 时按“是否显式设置”过滤默认值。
  • EventModel:事件模型,封装事件处理器与修饰符。
  • DirectiveModel:指令模型,封装指令名称、参数、修饰符、值与 v-for 迭代器等。
  • 协议 shared.ts:统一定义 JSON 值、JS 表达式/函数、数据类型、静态文件信息、扩展配置等基础类型。

架构总览

NodeModel 与 BlockModel 构成“节点-区块”的两级抽象:BlockModel 负责区块级元信息与节点集合管理,NodeModel 负责单个节点的属性、事件、指令与层级关系。渲染器通过 Provider 将 DSL 渲染为真实应用,同时监听节点与区块变更事件以实现增量更新。

sequenceDiagram
participant Dev as "开发者"
participant BM as "BlockModel"
participant NM as "NodeModel"
participant Prov as "Provider"
participant RD as "Renderer"
Dev->>BM : 创建/更新区块
BM->>NM : 构造/更新节点
NM->>NM : 更新属性/事件/指令
NM->>RD : 触发节点变更事件
BM->>RD : 触发区块变更事件
RD->>Prov : 生成渲染器与上下文
Prov-->>RD : 返回渲染器
RD->>RD : 渲染应用并挂载
RD->>Prov : 响应变更并更新DSL

详细组件分析

NodeModel 类图

NodeModel 是节点抽象的核心,内部组合了属性、事件、指令三种子模型,并通过事件总线对外广播变更。

classDiagram
class NodeModel {
+string id
+string name
+string from
+boolean invisible
+boolean locked
+any children
+slot
+props : Record<string, PropModel>
+events : Record<string, EventModel>
+directives : DirectiveModel[]
+boolean disposed
+constructor(schema, parent)
+update(schema, silent)
+setChildren(children, silent)
+setSlot(slot, silent)
+setProp(name, value, default, silent)
+removeProp(name, silent)
+getPropValue(name)
+setEvent(schema, silent)
+removeEvent(name, silent)
+setDirective(schema, silent)
+removeDirective(directive, silent)
+appendChild(node, silent)
+removeChild(node, silent)
+insertAfter(node, silent)
+insertBefore(node, silent)
+movePrev(silent)
+moveNext(silent)
+toDsl()
+dispose(silent)
+lock(silent)
+unlock(silent)
+setVisible(visible, silent)
+isChild(node) : boolean
+findChildIndex(child) : number
}
class PropModel {
+string name
+value
+defaultValue
+boolean isUnset
+setValue(value)
+getValue()
+static toDsl(props)
+static parse(props)
}
class EventModel {
+string name
+handler
+modifiers
+update(schema)
+static toDsl(events)
+static parse(events)
}
class DirectiveModel {
+string id
+name
+arg
+modifiers
+value
+iterator
+update(schema)
+static toDsl(directives)
+static parse(directives)
}
NodeModel --> PropModel : "组合"
NodeModel --> EventModel : "组合"
NodeModel --> DirectiveModel : "组合"

BlockModel 与 NodeModel 的关系

  • BlockModel 聚合多个 NodeModel,作为页面或区块的根容器。
  • BlockModel 提供节点的增删改移动与克隆能力,并在节点有父节点时委托给父节点,否则直接从自身节点列表移除。
  • BlockModel 与 NodeModel 共享事件总线,分别发出区块变更与节点变更事件,渲染器据此进行增量更新。
flowchart TD
A["BlockModel.addNode(node, target, position)"] --> B{"是否存在目标节点 target?"}
B -- 否 --> C["BlockModel.appendNode(node)"]
B -- 是 --> D{"position 为 inner?"}
D -- 是 --> E["target.appendChild(node)"]
D -- 否 --> F{"position 为 left/top?"}
F -- 是 --> G["target.parent ? target.insertAfter(node) : BlockModel.insertBefore(node, target)"]
F -- 否 --> H["target.parent ? target.insertAfter(node) : BlockModel.insertAfter(node, target)"]
I["BlockModel.removeNode(node)"] --> J{"node 是否有父节点?"}
J -- 是 --> K["node.dispose(silent)"]
J -- 否 --> L["node.dispose(true); BlockModel.__removeNode(node)"]

节点层级关系管理

  • 父子关系:NodeModel 维护 parent 字段;新增/删除子节点时自动维护 parent;销毁节点时从父节点移除。
  • 兄弟关系:通过 insertAfter/insertBefore/movePrev/moveNext 实现同级顺序调整;BlockModel 也提供相应能力。
  • 层级查询:isChild 支持递归判断某节点是否为当前节点的后代;findChildIndex 支持定位子节点索引。
sequenceDiagram
participant P as "父节点(NodeModel)"
participant C as "子节点(NodeModel)"
participant S as "兄弟节点(NodeModel)"
P->>P : appendChild(C)
C-->>P : parent = this
P->>P : insertAfter(S)
S-->>P : parent = this
P->>P : movePrev()/moveNext()
P->>P : removeChild(C)
C-->>P : parent = null

属性、事件与指令管理

  • 属性:PropModel 支持设置值与默认值,toDsl 时仅输出“已显式设置”的属性,避免冗余。
  • 事件:EventModel 支持更新处理器与修饰符,toDsl 输出事件定义。
  • 指令:DirectiveModel 支持指令名、参数、修饰符、值与 v-for 迭代器,toDsl 输出指令数组。
flowchart TD
Start(["调用 setProp/setEvent/setDirective"]) --> Check{"对象是否存在?"}
Check -- 是 --> Update["更新已有对象"]
Check -- 否 --> New["新建对象并加入集合"]
Update --> Emit["触发变更事件(非静默)"]
New --> Emit
Emit --> End(["结束"])

生命周期与状态同步

  • 创建:构造函数接收 NodeSchema,初始化 id/name/from,并调用 update 完成属性、事件、指令与子节点解析。
  • 更新:update 支持静默更新(不触发事件),常用于批量导入或初始化。
  • 销毁:dispose 递归销毁子节点,从父节点移除自身,清理静态注册表。
  • 可见性与锁定:setVisible/lock/unlock 支持对子树进行传播式更新,并触发变更事件。
flowchart TD
C["构造 NodeModel(schema)"] --> U["update(schema, silent=false)"]
U --> SC["setChildren(children, true)"]
U --> SS["setSlot(slot, true)"]
U --> PP["PropModel.parse(props)"]
U --> EE["EventModel.parse(events)"]
U --> DD["DirectiveModel.parse(directives)"]
U --> EV["触发变更事件(非静默)"]
D["dispose(silent)"] --> DC["递归 dispose 子节点"]
DC --> RP["从父节点移除"]
RP --> CL["标记 disposed=true 并从静态表移除"]

渲染引擎中的作用与虚拟 DOM 对应

  • 渲染器通过 Provider 将 BlockModel.toDsl 产出的 DSL 渲染为真实应用。
  • 渲染器订阅节点与区块变更事件,当 NodeModel 或 BlockModel 发生变化时,渲染器更新内部 DSL 并重新渲染。
  • 在设计器场景下,渲染器会在区块变更时重建应用并恢复选中状态。
sequenceDiagram
participant NM as "NodeModel"
participant BM as "BlockModel"
participant RD as "Renderer"
participant Prov as "Provider"
NM-->>RD : 触发节点变更事件
BM-->>RD : 触发区块变更事件
RD->>Prov : 生成渲染器与上下文
Prov-->>RD : 返回渲染器
RD->>RD : 渲染并挂载应用
RD->>Prov : 响应变更并更新DSL

依赖分析

  • NodeModel 依赖 PropModel、EventModel、DirectiveModel 三种子模型完成属性、事件与指令的建模。
  • BlockModel 依赖 NodeModel 构建节点树,并提供节点集合的增删改移动与克隆。
  • 协议 shared.ts 提供 JSON 值、JS 表达式/函数等基础类型,确保 NodeSchema/BlockSchema 的一致性。
  • 渲染器依赖 Provider 与事件总线,将模型层的变更映射到视图层。
graph LR
SH["shared.ts"] --> NM["NodeModel"]
SH --> BM["BlockModel"]
PM["PropModel"] --> NM
EM["EventModel"] --> NM
DM["DirectiveModel"] --> NM
NM --> BM
RD["Renderer"] --> BM
RD --> NM

性能考量

  • 静默更新:大量变更时建议使用 silent 参数,减少事件风暴与重复渲染。
  • 递归销毁:dispose 会递归销毁子树,注意在高频操作中避免频繁创建/销毁节点。
  • 变更粒度:尽量局部更新属性/事件/指令,避免不必要的整树刷新。
  • 渲染增量:渲染器基于事件总线进行增量更新,保持 DSL 的响应式特性。

故障排查指南

  • 节点无法插入/移动:检查父节点 children 类型是否为数组,以及节点是否已被销毁。
  • 事件未触发:确认调用时 silent 参数是否为 true,或是否正确订阅了事件总线。
  • 子树状态异常:检查 setVisible/lock/unlock 是否被正确传播至子树。
  • 渲染不更新:确认渲染器是否订阅了节点/区块变更事件,以及 DSL 是否被正确更新。

结论

NodeModel 以清晰的抽象与完善的子模型体系,为低代码平台提供了稳定、可扩展的节点建模能力。结合 BlockModel 的区块级管理与渲染器的事件驱动更新,形成从模型到视图的高效闭环。通过合理的 API 使用与扩展机制,开发者可以快速构建自定义节点类型并融入整体生态。

附录

API 参考(NodeModel)

  • 构造与更新
    • 构造函数:接收 NodeSchema 与可选父节点,初始化并解析 schema
    • update(schema, silent?):更新节点属性,支持静默更新
    • toDsl():序列化为 NodeSchema
  • 子节点管理
    • setChildren(children, silent?):设置子节点(数组或表达式)
    • appendChild(node, silent?):追加子节点
    • removeChild(node, silent?):移除子节点
    • insertAfter(node, silent?) / insertBefore(node, silent?):插入兄弟节点
    • movePrev(silent?) / moveNext(silent?):在同级移动
    • isChild(node):判断是否为后代
    • findChildIndex(child):查找子节点索引
  • 属性管理
    • setProp(name, value, default?, silent?)
    • removeProp(name, silent?)
    • getPropValue(name)
  • 事件管理
    • setEvent(schema, silent?)
    • removeEvent(name, silent?)
  • 指令管理
    • setDirective(schemaOrModel, silent?)
    • removeDirective(directive, silent?)
  • 可见性与锁定
    • setVisible(visible, silent?)
    • lock(silent?) / unlock(silent?)
  • 销毁
    • dispose(silent?)

API 参考(BlockModel)

  • 构造与更新
    • 构造函数:接收 BlockSchema,初始化并解析 nodes
    • update(schema, silent?):更新区块属性,支持静默更新
    • toDsl(version?):序列化为 BlockSchema
  • 节点管理
    • addNode(node, target?, position?, silent?):添加节点(支持 left/right/top/bottom/inner)
    • removeNode(node, silent?):删除节点(区分是否有父节点)
    • move(node, target?, position?, silent?):移动节点
    • movePrev(node, silent?) / moveNext(node, silent?):在区块级移动
    • cloneNode(target, silent?):克隆节点
  • 区块级属性与功能
    • setState(name, value, silent?) / removeState(name, silent?)
    • setFunction(type, name, value, silent?) / removeFunction(type, name, silent?)
    • setCss(content, silent?)
    • setWatch(watch, silent?) / removeWatch(watch, silent?)
    • setProp(prop, silent?) / removeProp(prop, silent?)
    • setEmit(emit, silent?) / removeEmit(emit, silent?)
    • setExpose(expose, silent?)
    • setSlot(slot, silent?) / removeSlot(slot, silent?)
    • setInject(inject, silent?) / removeInject(inject, silent?)
    • setDataSource(source, silent?) / removeDataSource(name, silent?)
  • 销毁
    • dispose()

协议与类型

  • JSONValue、JSExpression、JSFunction、NodeProps、NodeEvent、NodeDirective 等基础类型定义于 shared.ts,为 NodeModel/BlockModel 的输入输出提供约束。

参考资料

VTJ.PRO 是一个开源的、AI 驱动的 Vue 3 企业级应用开发平台。它通过 AI 智能体与可视化编排实现高效开发,并支持导出标准 Vue 代码以避免平台锁定。更多信息请访问: