配置与数据分离:一种可视化搭建的属性编辑方案

我们到底在折腾什么?

可视化搭建工具里,最烦人的就是深层属性的修改。比如改一个图片的边框宽度,路径可能是 style.border.width,老办法是把整个 style 对象读出来,改完再整个塞回去。一不小心就把别的属性弄丢了,性能也烂,整个组件树跟着重绘。

后来我们想,能不能做到:改哪个叶子,就只动那一个叶子,跟它隔壁的属性井水不犯河水。而且,怎么让配置(这个属性怎么编辑)和真实数据(用户填的值)彻底分家,各自清爽。

于是就有了下面这套思路。


核心想法:两棵长得一样的树

整个系统里其实有两棵对象树,结构完全一致:

  1. 配置树(Config Tree) :负责告诉编辑器"这个属性长什么样"------是输入框、下拉、颜色选择器?是不是只读?有没有最大值限制?联动规则是啥?
  2. 真实值树(Value Tree) :只存用户填进去的实实在在的数据,没有任何多余的东西。

它们的区别只有一点:配置树的每个父子关系之间,多了一个叫 children 的字段来连接。

举个栗子:

json 复制代码
// 配置树(骨架+元信息)
{
  "page": {
    "type": "Page",
    "children": {
      "logo": {
        "type": "Image",
        "children": {
          "src": { "type": "AssetPicker", "label": "图片地址", "required": true },
          "width": { "type": "NumberInput", "label": "宽度", "min": 1 }
        }
      }
    }
  }
}

对应的真实值树:

css 复制代码
{
  "page": {
    "logo": {
      "src": "/my-logo.png",
      "width": 200
    }
  }
}

看到没?结构一模一样,只是真实值去掉了 children 和所有描述性的字段,只剩下纯数据。


路径统一:一个 key 打通两棵树

有了这个同构关系,路径规则就简单到爆:

  • 逻辑路径 (我们心里想的节点链):page.logo.src
  • 在配置树里找 :把点换成 .children.,得到 page.children.logo.children.src,就能一路访问到配置节点。
  • 在真实值树里找 :直接用 page.logo.src 去取数据。

这就意味着,一个逻辑路径 page.logo.src 既是一个配置节点的唯一标识,又是它在真实值里的位置。双向映射零成本,再也没有那种"配置路径"和"数据路径"对不上的噩梦。


编辑时怎么玩?注入 value 和 pathKey

初始化的时候,我们拿到原始配置,再读一下真实值,给每个叶子节点塞两个字段:

  • value:当前值(从真实值里取,或默认值)
  • pathKey:就是这个节点的逻辑路径,比如 "page.logo.src"

于是运行时配置变成这样:

css 复制代码
{
  "page": {
    "children": {
      "logo": {
        "children": {
          "src": {
            "type": "AssetPicker",
            "label": "图片地址",
            "value": "/my-logo.png",   // 从真实值拿的
            "pathKey": "page.logo.src"
          },
          "width": {
            "type": "NumberInput",
            "label": "宽度",
            "value": 200,
            "pathKey": "page.logo.width"
          }
        }
      }
    }
  }
}

用户在属性面板上改 width,我们直接改这个节点的 value 为 300,然后因为知道它的 pathKey 是 page.logo.width,就可以立即执行:

ini 复制代码
realValue.page.logo.width = 300;

画布上的那个组件也就只重新渲染宽度变化的部分,别的纹丝不动。完美。


为什么不能直接用真实值渲染属性面板?

你可能会问:那干脆直接用真实值对象来生成面板呗,干嘛多此一举搞个配置树?

因为真实值里你分不清一个对象到底是"需要递归的子节点",还是"一个普通的样式对象"。比如 style: { width: 100 },它只是个数据容器,不是子组件。但 logo: { src: '...' } 却是一个实实在在的子节点。

配置树用 children 字段清晰地标出了边界:有 children 的才是组件节点,需要递归;没有 children 的对象只是普通属性值。渲染器就靠这个"骨架"来决策什么时候递归,什么时候停。


还有什么好玩的?

  • 联动超级简单 :配置节点里可以加 dependencies 字段,写上依赖的 pathKey,值一变,自己重新算一遍再写回真实值,整个链条自动运转。
  • 面板渲染递归化 :属性面板直接根据配置树的 children 递归渲染,每个节点根据 type 选择控件,值绑定到 value,只读、必填等约束直接生效。
  • 性能炸裂 :每一个 value 修改只触发对应 pathKey 的响应,画布重绘粒度精准到组件属性级,大规模页面也不卡。

总结成一句话

我们用两棵同构的对象树分离了"编辑规则"与"数据内容",通过在配置树节点间插入 children 来标记层级,再用统一逻辑路径映射到真实值树,实现了属性级别的精准编辑和渲染。

简单、干净,没有任何多余转换,就是纯粹的"两个长得一样的对象,一个带 children 一个不带,路径互推"。

相关推荐
今年下半年3 分钟前
【前端】Vue2 项目 ECharts 统计图表组件封装与使用实践(附组件源码)
前端·javascript·echarts
冒险的豆子7 分钟前
JS 原型链 通俗易懂 解释说明(类比 关系)
开发语言·javascript·原型模式
西索斯coding11 分钟前
在 中接入 教程:p配置、m填写、扩展思考参数一篇就够(2)
java·前端·javascript·ai
代码猎人14 分钟前
前端怎么优化项目基础架构
前端
__sjfzllv___22 分钟前
在职前端Leader学习/转行 AI Agent -DAY78
前端
代码猎人22 分钟前
前端怎么封装工具类
前端
deli00726 分钟前
PDF 合并别再用在线工具:12 份文件浏览器里合完,实测不上传
前端
shuoshuohaohao26 分钟前
SpringBoot3-web开发-静态资源-内容协商
前端
kiros_wang28 分钟前
RDB关系数据库实战:增删改查、事务、分页、版本迁移
前端
用户17335980753730 分钟前
纯前端 PDF 双面打印避坑指南:翻页顺序错了整个白印?
前端·javascript·vue.js