【低代码】前端低代码开发日记2:遇到的问题(1)双向绑定

4 阅读3分钟

在前期的快速迭代阶段,虽然界面有些杂乱,但整体功能尚能凑合运行。真正让人头疼的,还是接下来几个关键功能的实现。

遇到的问题

双向绑定

在Vue中,v-model提供了方便的双向绑定功能,它是modelValue属性和onUpdate:modelValue事件的语法糖。然而,我的低代码开发基于JSON配置,每个细节都需要通过代码控制,因此我是通过Vue的h函数进行渲染,想要实现类似v-model的双向绑定体验却相当困难。经过多次版本迭代,才勉强实现了基本的双向绑定。

示例如下:

{
    "id": "component_1xxx",
    "label": "组件",
    "uiType": "component",
    "uiName": "组件",
    "domType": "div",
    "children": [
        {
            "id": "component_2xxx",
            "uiType": "component",
            "domType": "el-input",
            "attrs": [
                {
                    "label": "输入值",
                    "key": "modelValue",
                    "type": "string",
                    "value": "",
                    "id": "modelValue_1xxx"
                }
            ]
        }
    ],
    "data": [
        {
            "id": "var_1xxx",
            "label": "变量_1",
            "value": "变量1值"
        }
    ],
    "bind": {
        "modelValue_1xxx": "var_1xxx",
    }
}

这个配置的意思是,将el-input组件的“输入值”属性与“变量_1”绑定。

为了管理这种绑定关系,我在最上层的bind字段中维护变量绑定关系。当el-input的值变化时,我需要通过onUpdate:modelValue事件,先查找bind中是否有绑定关系,再修改data中的变量值。整个过程繁琐,必须不断地向上触发emit事件,直到最顶层处理。这种设计不仅冗长,层级深时还容易出错,甚至影响性能。

我曾考虑通过inject的方式,将最外层配置传递到子组件中,减少事件触发的层级,但最终放弃了。

新的解决方案

在新版本中,我提出了一个新的思路:在顶层创建一个当前组件或页面的实例(VNode)上下文(context),在创建VNode时初始化所有变量,并将其传递给子组件,让每个组件自己去处理和修改变量值。

这个思路已经在工作项目中实现了一部分不经常变化的页面动态化,至今为止,用户还没有发现这些页面是通过低代码配置生成的。

示例代码:

  // 创建应用实例但不挂载到 DOM 中
  const app = createApp(MyComponent);
  // 使用一个虚拟的 DOM 元素来挂载
  const vnodeContainer = document.createElement("div");
  document.body.appendChild(vnodeContainer); // 需要在文档中才能完成挂载
  runtimeInstance.value = app.mount(vnodeContainer);
  // 移除挂载点从而使组件不显示在页面上
  app.unmount();
  console.log("instance: ", runtimeInstance); // 可以访问组件实例

这种设计有一个明显的好处,就是将运行时状态与配置(JSON)完全隔离开。运行时的变化只影响运行时的对象,而不会对JSON配置产生任何改动。对低代码开发而言,JSON配置就是“程序”,运行时的变化不应该直接修改配置数据。这样,即便页面崩溃,也能快速恢复到初始状态。

持续的难题

尽管引入了VNode上下文的方式解决了一些问题,但bind字段的绑定方式依然存在,无法彻底抛弃。这增加了代码的复杂度,涉及到多个方面的逻辑,改造起来依然面临诸多挑战。

体验最新的轻构低代码

账号:test 密码:123456