组件联动是指几个组件互相关联。也就是当一个组件状态变动时,其余组件能够响应。
组件联动是多对多关系的,且目标分为一次性与持续性:
- 多对多关系:即一个组件能够同时被多个组件联动;多个组件能够同时联动一个组件。
- 一次性与持续性:一次性事件能够被笼罩,持续性事件会同时失效,且要思考叠加关系。
肯定水平上,持续性事件能够笼罩一次性事件的场景:组件永远响应最初一个过去的事件即可。
接下来咱们引入 组件值 与 值联动 两个概念,来实现持续性联动性能。
组件值
每个组件实例都有一个惟一的组件值。
咱们能够通过 getValue(componentId)
与 setValue(componentId, value)
拜访或更新组件值:
const table = {
componentName: "table",
runtimeProps: ({componentId, setValue}) => ({
// 给组件注入 onChange 函数,在其触发时更新以后组件实例的组件值
onChange: (value) => setValue(componentId, value),
}),
};
也能够通过 componentMeta.value
申明组件值,比方上面的例子,让组件值与 props.value
同步:
const table = {
componentName: "table",
// 申明 value 的值为组件 props.value 的返回值,并随着组件 props.value 的更新而更新
value: ({selector}) => selector(({props}) => props.value),
};
以上两种形式任选一种应用即可。
为什么一个组件实例只有一个组件值?
一个组件可能同时领有多个状态,比方该组件外部有一个输入框,还有一个按钮,可能输入框的值,与按钮的点击状态都会对其余组件产生联动成果。但这并不意味着一个组件实例须要多个组件值,咱们能够将组件值定义为对象,并正当布局不同的 key 形容不同维度的值:
// 组件值构造
{
// 组件内输入框的值
text: '123',
// 组件内按钮被按下的次数
buttonClickTimes: false
}
为什么不必 props.value
代替组件值?
实践上能够,但这样限定了组件对 props 的定义。兴许有的组件用 props.value
形容输入框的值,但也有比方 Check 组件,用 props.checked
示意以后选中状态。只有形象一个定义与组件元信息的规定,让业务自在对接,才能够让组件值适配任意类型的组件。
值联动
有了组件值这个概念,就能够以组件实例为粒度,设计组件的关联关系了。
为了让组件关联更加灵便,咱们的设计须要满足以下几种能力:
- 联动关系反对多对多。
- 能够随着全局数据状态变动,或者组件本身 props 变动,随时扭转组件关联关系。
- 一个组件能够定义其余几个组件的关联关系,哪怕本人不参加到联动关系链中。
- 当组件实例被删除时,由它定义的联动关系立即生效。
估咱们采纳 componentMeta.valueRelates
申明式定义值联动关系:
const table = {
componentName: "table",
valueRelates: ({componentId, selector}) => {
return [
{
sourceComponentId: componentId, // 本人为触发源
targetComponentId: selector(({props}) => props.targetComponentId), // 指标组件 ID 为 props.targetComponentId
},
];
},
};
这样设计能够同时满足以上四个要求,解释如下:
- 能够在任意组件实例定义多个联动关系,天然能够实现多对多联动。
valueRelates
引入selector
能够响应state
或props
的变动,能够由任意状态驱动联动关系更新。- 如果
source
与target
都不指向本人,则本人不参加到联动关系链中。 - 申明式定义形式,天然在组件实例被销毁时生效。
那么组件如何响应联动呢?重点就在这里,组件能够通过 selector(({relates}) =>)
的 relates
拿到本人以后的联动状态,比方:
const table = {
componentName: "table",
runtimeProps: ({selector}) => {
// relates 构造如下,对于每一个作用于本人的组件实例 ID 与最新 value 值都能够拿到
// [{
// sourceComponentId: 'abc',
// value: '123'
// }]
const relates = selector(({relates}) => relates);
return {status: relates.length > 0 ? "linked" : "free",};
},
};
如果咱们在 runtimeProps
里应用 selector
监听 relates
,就能够在联动状态变动时,驱动组件渲染,并传入联动相干状态;如果在 fetcher
里应用 selector
监听 relates
,就能够在联动状态变动时,驱动组件触发查问,等等。
当前咱们拓展越来越多的组件元信息回调函数,反对了 selector
之后,都能够申明式的响应 relates
变动,也就是组件能够申明式灵便响应联动,真正意义上让联动能够用在任何场景。
框架没有对联动做太多的联动内置行为,实现的都是灵便规定,尽管业务须要补全不少申明,但胜在灵便与用法对立。
形容联动行为
不同的联动可能做不同的事,比方一个输入框组件,可能同时有以下两种作用:
- 让另一个组件查问条件减少 “where name=” 以后输入框的值。
- 当组件的值为 “delete” 时,让画布另一个组件暗藏。
为了辨别联动的性能,能够在 valueRelates
减少 payload
参数,形容该联动的目标:
const table = {
componentName: "table",
valueRelates: ({componentId, selector}) => {
return [
{
sourceComponentId: componentId,
targetComponentId: selector(({props}) => props.targetComponentId),
// 作用为指标组件的查问筛选条件
payload: "filter",
},
{
sourceComponentId: componentId,
targetComponentId: selector(({props}) => props.targetComponentId),
// 作用为指标组件是否暗藏
payload: "hide",
},
];
},
};
而后指标组件就能够依据理论状况,在 fetcher
过滤 relates
中 payload="filter"
的值,在 runtimeProps
过滤 relates
中 payload="hide"
的值。
用继续联动实现一次性联动
每一次组件更新 value 值后,都会刷新对指标组件 relates
的地位,具体来说,会将其置顶,所以指标组件能够依据 relates
先来后到程序判断,比方在联动成果抵触时,让排在后面的优先失效。
比方:
const table = {
componentName: "table",
runtimeProps: ({selector}) => {
// 找到最后失效的,payload 为 color 的联动,笼罩 props.color
const relateColor = selector(({relates}) =>
relates.find((each) => each.payload === "color")
);
return {color: relateColor,};
},
};
当另一个组件触发 value 变动时,它会排在指标组件 relates
最后面,这样的话,如果指标组件依照如上形式编写响应代码,就总会响应最初一次失效的联动。
总结
这一节介绍了如何设置联动,并引出了组件值概念。
在框架层定义形象的组件值概念,并通过申明式或调用式对接到 state
状态或组件 props
,这种形象理念会贯通整个框架的设计过程。类似的 valueRelates
也具备申明式能力,并将联动作用通过 selector
的 relates
对象传递给组件实例应用,让联动的生产灵便度大大增加。
可视化搭建框架设计思路可能都大同小异,但惋惜的是,许多搭建框架都对比方联动、查问等场景做了定制化束缚,使每个框架或多或少存在着公有协定,而我在这个系列想强调的是,能够进一步形象,让框架提供业务自在定义协定的能力,而不是提供某个固定的协定。
探讨地址是:精读《组件值与联动》· Issue #469 · dt-fe/weekly
如果你想参加探讨,请 点击这里,每周都有新的主题,周末或周一公布。前端精读 – 帮你筛选靠谱的内容。
关注 前端精读微信公众号
<img width=200 src=”https://img.alicdn.com/tfs/TB165W0MCzqK1RjSZFLXXcn2XXa-258-258.jpg”>
版权申明:自在转载 - 非商用 - 非衍生 - 放弃署名(创意共享 3.0 许可证)