ascoders / weekly

前端精读周刊。帮你理解最前沿、实用的技术。
28.74k stars 3.25k forks source link

可视化搭建 - 组件值与联动 #469

Closed ascoders closed 1 year ago

ascoders commented 1 year ago

组件间可能存在联动关系,比如筛选器作用于图表查询、组件间联动响应等等。这些功能可以由业务代码用事件通信等方式实现,但作为可视化搭建框架,提供一个内置好用,且好调试的内置联动方案是比较必要的。


可视化搭建 - 组件值与联动

yanghuanrong commented 1 year ago

每次阅读,都像是对工作的一次复盘

cMing1997 commented 1 year ago

个人认为,可视化中组件与组件值之间的更新、联动要想做的比较好的前提是,对于可变值的数据模型定义需要考虑的很全面才行,在很多时候,很简单的一个逻辑更新 如果碰到很好的数据模型设计,那可能就是一个链条更新过去,但是如果碰到设计的很冗余的设计,就需要多层转化,反而适得其反,不仅不利于功能维护迭代,还很容易出现边界bug之类的

tzstone commented 1 year ago

setValue/getValue是底层公共方法还是每个组件自己暴露的方法? 如果是公共方法, 由于组件值没有在props里固化, 应该取不到值? 如果是每个组件自己暴露的方法, 那值联动的时候是根据componentId去调用组件的方法取值? 另外valueRelates是定义在组件meta吗? 这样联动关系是不是无法在编辑期间灵活修改?

ascoders commented 1 year ago

@tzstone

  1. setValue/getValue 是底层公共方法,比如 setValue(id, 'ok'),可以把 ID 为 {id} 的组件 value 更新为 'ok'.
  2. 组件值就是故意设计不在 props 固化的,因为有些时候组件值不是从 props 推导的,比如就在内存里,或者由全局状态 state.xx 衍生出来。如果想要由 props 固化也很简单,在组件元信息定义 { props: ({ selector }): selector(({ props })) => props?.value} 就可以把组件 value 绑到 props 上,以后改 props.value 就等于改组件 value。
  3. valueRelates 是定义在组件 meta 的,还是一个道理,通过 selector 可以很方便的绑定到任何属性,包括 props。selector 有几个参数:
    • state: 全局状态。
    • props: 组件实例的 props。
    • mergedProps: 包括 runtimeProps 在内的运行时最终 props。
    • value: 组件 value。
    • relates: 组件被关联信息。

非常有想象空间,你甚至可以把联动关系存在画布外自定义的数据流,通过 selector 从 state 上给每个组件绑定 valueRelates 关系。也可以绑定关系就存在每个组件实例上,通过 selector 从 props 上绑定 valueRelates。

ascoders commented 1 year ago

这个设计主打的就是一个响应式,只要绑定了,任何时候改了 selector 内用到的值,就会立刻让使用处响应。比如在 meta.valueRelates 里用了 selector 绑定了 props,那么编辑状态改了组件 props 就可以立刻触发 valueRelates 的变化,进而触发后续一系列变化,比如被关联的组件重新取数等等。

tzstone commented 1 year ago

@tzstone

  1. setValue/getValue 是底层公共方法,比如 setValue(id, 'ok'),可以把 ID 为 {id} 的组件 value 更新为 'ok'.
  2. 组件值就是故意设计不在 props 固化的,因为有些时候组件值不是从 props 推导的,比如就在内存里,或者由全局状态 state.xx 衍生出来。如果想要由 props 固化也很简单,在组件元信息定义 { props: ({ selector }): selector(({ props })) => props?.value} 就可以把组件 value 绑到 props 上,以后改 props.value 就等于改组件 value。
  3. valueRelates 是定义在组件 meta 的,还是一个道理,通过 selector 可以很方便的绑定到任何属性,包括 props。selector 有几个参数:
  • state: 全局状态。
  • props: 组件实例的 props。
  • mergedProps: 包括 runtimeProps 在内的运行时最终 props。
  • value: 组件 value。
  • relates: 组件被关联信息。

非常有想象空间,你甚至可以把联动关系存在画布外自定义的数据流,通过 selector 从 state 上给每个组件绑定 valueRelates 关系。也可以绑定关系就存在每个组件实例上,通过 selector 从 props 上绑定 valueRelates。

关于setValue/getValue还有一点不解. value我的理解是组件的内部状态外部化, 是运行时产生的, 存放在跟props同级的一个属性. getValue是公共方法的话没问题, 因为根据组件id就能取到对应的value, 但如果setValue也是公共方法的话, 通过id更改组件value, 这个变更是不是也应该修改到组件的内部状态? 这样的话组件本身是不是需要监听value的变化? 但是用户操作造成组件内部状态变化也会修改value值(比如切换筛选项), 这样会不会循环了?

tzstone commented 1 year ago

@tzstone

  1. setValue/getValue 是底层公共方法,比如 setValue(id, 'ok'),可以把 ID 为 {id} 的组件 value 更新为 'ok'.
  2. 组件值就是故意设计不在 props 固化的,因为有些时候组件值不是从 props 推导的,比如就在内存里,或者由全局状态 state.xx 衍生出来。如果想要由 props 固化也很简单,在组件元信息定义 { props: ({ selector }): selector(({ props })) => props?.value} 就可以把组件 value 绑到 props 上,以后改 props.value 就等于改组件 value。
  3. valueRelates 是定义在组件 meta 的,还是一个道理,通过 selector 可以很方便的绑定到任何属性,包括 props。selector 有几个参数:
  • state: 全局状态。
  • props: 组件实例的 props。
  • mergedProps: 包括 runtimeProps 在内的运行时最终 props。
  • value: 组件 value。
  • relates: 组件被关联信息。

非常有想象空间,你甚至可以把联动关系存在画布外自定义的数据流,通过 selector 从 state 上给每个组件绑定 valueRelates 关系。也可以绑定关系就存在每个组件实例上,通过 selector 从 props 上绑定 valueRelates。

关于setValue/getValue还有一点不解. value我的理解是组件的内部状态外部化, 是运行时产生的, 存放在跟props同级的一个属性. getValue是公共方法的话没问题, 因为根据组件id就能取到对应的value, 但如果setValue也是公共方法的话, 通过id更改组件value, 这个变更是不是也应该修改到组件的内部状态? 这样的话组件本身是不是需要监听value的变化? 但是用户操作造成组件内部状态变化也会修改value值(比如切换筛选项), 这样会不会循环了?

另外还有个问题, selector本身是作为runtimeProps函数的一个入参, 但你上面说selector的入参又包含了mergedProps, 这个应该是运行完runtimeProps后才产生的吧? 这样是不是冲突了?

ascoders commented 1 year ago

@tzstone selector 是有循环问题,所以在 runtimeProps: () 访问的 selector 中是没有 mergedProps 这个属性的,背后有一个执行顺序在保证。

关于 meta.value(), setValue() 这些概念我至今为止没有碰到冲突或者循环的情况。当然如果用 meta.value() 绑定了组件值,再去 setValue 肯定就不合适了,一般要么受控,要么非受控,不要既受控又 setValue。如果非要这么用也不会报错,因为 meta.value() 只会在 selector 产生变化时重新执行,所以 setValue 可以在 selector 内容变化前生效一段时间。

tzstone commented 1 year ago

@tzstone selector 是有循环问题,所以在 runtimeProps: () 访问的 selector 中是没有 mergedProps 这个属性的,背后有一个执行顺序在保证。

关于 meta.value(), setValue() 这些概念我至今为止没有碰到冲突或者循环的情况。当然如果用 meta.value() 绑定了组件值,再去 setValue 肯定就不合适了,一般要么受控,要么非受控,不要既受控又 setValue。如果非要这么用也不会报错,因为 meta.value() 只会在 selector 产生变化时重新执行,所以 setValue 可以在 selector 内容变化前生效一段时间。

重新看了遍文章, 对于setValue, 其实是不是只要保证value和组件内部状态保持同一个对象引用, 就没有我上面说的需要监听value变化去设置组件内部状态的问题了? PS: 话说大佬有计划写个小demo演示整个可视化搭建的设计吗? 看文章时可能会比较容易理解整个设计思想.

tzstone commented 1 year ago

value的问题解决了, 原来一直错误以为value应该是组件树节点上的一个属性, 实际上应该把value当成组件实例的一个属性, 比如作为vue组件的一个计算属性. 这样setValue/getValue都是直接对组件实例的内部状态进行操作, 也就没有了所谓的监听环节.

tzstone commented 1 year ago

这个设计主打的就是一个响应式,只要绑定了,任何时候改了 selector 内用到的值,就会立刻让使用处响应。比如在 meta.valueRelates 里用了 selector 绑定了 props,那么编辑状态改了组件 props 就可以立刻触发 valueRelates 的变化,进而触发后续一系列变化,比如被关联的组件重新取数等等。

请教下selector在react中是用哪种技术实现的? 我们用的是vue, 在vue里面做到这一点大概就是把selector的callback入参作为watch函数的第一个入参, 在第二个入参去做selector内用到的值变化后的响应回调. 但即使这样仍有问题, 因为selector不知道它是被谁调用的, 也即在watch函数的第二个入参里并不知道要重新执行什么(如runtimeProps/valueRelates). 不知道是不是我理解有误?

rictt commented 1 year ago

组件值联动这块,文章描述是放在meta里面呢?是不是不太合适,或者我理解有问题

我的理解:每个组件跟其他组件的联动关系是不确定的、动态的(可配置),meta用来定义一些组件元信息,所以valueRelates放在componentTree的props上会不会更好一点呀?

ascoders commented 1 year ago

组件值联动这块,文章描述是放在meta里面呢?是不是不太合适,或者我理解有问题

放在 meta 不代表联动关系就是静态的了,核心是根据 selector 机制,可以在 meta 定义,把联动关系绑定到 props 上。这比直接设定一个默认的 props 绑定规则好的地方是,我们可以通过修改 meta 自定义绑定规则。