前端可視化搭建組件值與聯(lián)動實現(xiàn)詳解
正文
組件聯(lián)動是指幾個組件相互關聯(lián)。也就是當一個組件狀態(tài)變化時,其他組件可以響應。
組件聯(lián)動是多對多關系的,且目的分為一次性與持續(xù)性:
- 多對多關系:即一個組件可以同時被多個組件聯(lián)動;多個組件可以同時聯(lián)動一個組件。
- 一次性與持續(xù)性:一次性事件可以被覆蓋,持續(xù)性事件會同時生效,且要考慮疊加關系。
一定程度上,持續(xù)性事件可以覆蓋一次性事件的場景:組件永遠響應最后一個過來的事件即可。
接下來我們引入 組件值 與 值聯(lián)動 兩個概念,來實現(xiàn)持續(xù)性聯(lián)動功能。
組件值
每個組件實例都有一個唯一的組件值。
我們可以通過 getValue(componentId) 與 setValue(componentId, value) 訪問或更新組件值:
const table = {
componentName: "table",
runtimeProps: ({ componentId, setValue }) => ({
// 給組件注入 onChange 函數(shù),在其觸發(fā)時更新當前組件實例的組件值
onChange: (value) => setValue(componentId, value),
}),
};
也可以通過 componentMeta.value 聲明組件值,比如下面的例子,讓組件值與 props.value 同步:
const table = {
componentName: "table",
// 聲明 value 的值為組件 props.value 的返回值,并隨著組件 props.value 的更新而更新
value: ({ selector }) => selector(({ props }) => props.value),
};
以上兩種方式任選一種使用即可。
為什么一個組件實例只有一個組件值?
一個組件可能同時擁有多個狀態(tài),比如該組件內(nèi)部有一個輸入框,還有一個按鈕,可能輸入框的值,與按鈕的點擊狀態(tài)都會對其他組件產(chǎn)生聯(lián)動效果。但這并不意味著一個組件實例需要多個組件值,我們可以將組件值定義為對象,并合理規(guī)劃不同的 key 描述不同維度的值:
// 組件值結(jié)構(gòu)
{
// 組件內(nèi)輸入框的值
text: '123',
// 組件內(nèi)按鈕被按下的次數(shù)
buttonClickTimes: false
}
為什么不用 props.value 代替組件值?
理論上可以,但這樣限定了組件對 props 的定義。也許有的組件用 props.value 描述輸入框的值,但也有比如 Check 組件,用 props.checked 表示當前選中狀態(tài)。只有抽象一個定義與組件元信息的規(guī)則,讓業(yè)務自由對接,才可以讓組件值適配任意類型的組件。
值聯(lián)動
有了組件值這個概念,就可以以組件實例為粒度,設計組件的關聯(lián)關系了。
為了讓組件關聯(lián)更加靈活,我們的設計需要滿足以下幾種能力:
- 聯(lián)動關系支持多對多。
- 可以隨著全局數(shù)據(jù)狀態(tài)變化,或者組件自身 props 變化,隨時改變組件關聯(lián)關系。
- 一個組件可以定義其他幾個組件的關聯(lián)關系,哪怕自己不參與到聯(lián)動關系鏈中。
- 當組件實例被刪除時,由它定義的聯(lián)動關系立刻失效。
估我們采用 componentMeta.valueRelates 聲明式定義值聯(lián)動關系:
const table = {
componentName: "table",
valueRelates: ({ componentId, selector }) => {
return [
{
sourceComponentId: componentId, // 自己為觸發(fā)源
targetComponentId: selector(({ props }) => props.targetComponentId), // 目標組件 ID 為 props.targetComponentId
},
];
},
};
這樣設計可以同時滿足以上四個要求,解釋如下:
- 可以在任意組件實例定義多個聯(lián)動關系,自然可以實現(xiàn)多對多聯(lián)動。
valueRelates引入selector可以響應state或props的變化,可以由任意狀態(tài)驅(qū)動聯(lián)動關系更新。- 如果
source與target都不指向自己,則自己不參與到聯(lián)動關系鏈中。 - 聲明式定義方式,自然在組件實例被銷毀時失效。
那么組件如何響應聯(lián)動呢?重點就在這里,組件可以通過 selector(({ relates }) =>) 的 relates 拿到自己當前的聯(lián)動狀態(tài),比如:
const table = {
componentName: "table",
runtimeProps: ({ selector }) => {
// relates 結(jié)構(gòu)如下,對于每一個作用于自己的組件實例 ID 與最新 value 值都可以拿到
// [{
// sourceComponentId: 'abc',
// value: '123'
// }]
const relates = selector(({ relates }) => relates);
return {
status: relates.length > 0 ? "linked" : "free",
};
},
};
如果我們在 runtimeProps 里使用 selector 監(jiān)聽 relates,就可以在聯(lián)動狀態(tài)變化時,驅(qū)動組件渲染,并傳入聯(lián)動相關狀態(tài);如果在 fetcher 里使用 selector 監(jiān)聽 relates,就可以在聯(lián)動狀態(tài)變化時,驅(qū)動組件觸發(fā)查詢,等等。
以后我們拓展越來越多的組件元信息回調(diào)函數(shù),支持了 selector 之后,都可以聲明式的響應 relates 變化,也就是組件可以聲明式靈活響應聯(lián)動,真正意義上讓聯(lián)動可以用在任何場景。
框架沒有對聯(lián)動做太多的聯(lián)動內(nèi)置行為,實現(xiàn)的都是靈活規(guī)則,雖然業(yè)務需要補全不少聲明,但勝在靈活與用法統(tǒng)一。
描述聯(lián)動行為
不同的聯(lián)動可能做不同的事,比如一個輸入框組件,可能同時有以下兩種作用:
- 讓另一個組件查詢條件增加 "where name=" 當前輸入框的值。
- 當組件的值為 "delete" 時,讓畫布另一個組件隱藏。
為了區(qū)分聯(lián)動的功能,可以在 valueRelates 增加 payload 參數(shù),描述該聯(lián)動的目的:
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",
},
];
},
};
然后目標組件就可以根據(jù)實際情況,在 fetcher 過濾 relates 中 payload="filter" 的值,在 runtimeProps 過濾 relates 中 payload="hide" 的值。
用持續(xù)聯(lián)動實現(xiàn)一次性聯(lián)動
每一次組件更新 value 值后,都會刷新對目標組件 relates 的位置,具體來說,會將其置頂,所以目標組件可以根據(jù) relates 先來后到順序判斷,比如在聯(lián)動效果沖突時,讓排在前面的優(yōu)先生效。
比如:
const table = {
componentName: "table",
runtimeProps: ({ selector }) => {
// 找到最初生效的,payload 為 color 的聯(lián)動,覆蓋 props.color
const relateColor = selector(({ relates }) =>
relates.find((each) => each.payload === "color")
);
return {
color: relateColor,
};
},
};
當另一個組件觸發(fā) value 變化時,它會排在目標組件 relates 最前面,這樣的話,如果目標組件按照如上方式編寫響應代碼,就總會響應最后一次生效的聯(lián)動。
總結(jié)
這一節(jié)介紹了如何設置聯(lián)動,并引出了組件值概念。
在框架層定義抽象的組件值概念,并通過聲明式或調(diào)用式對接到 state 狀態(tài)或組件 props,這種抽象理念會貫穿整個框架的設計過程。相似的 valueRelates 也具有聲明式能力,并將聯(lián)動作用通過 selector 的 relates 對象傳遞給組件實例使用,讓聯(lián)動的消費靈活度大大增加。
可視化搭建框架設計思路可能都大同小異,但可惜的是,許多搭建框架都對比如聯(lián)動、查詢等場景做了定制化約束,使每個框架或多或少存在著私有協(xié)議,而我在這個系列想強調(diào)的是,可以進一步抽象,讓框架提供業(yè)務自由定義協(xié)議的能力,而不是提供某個固定的協(xié)議。
討論地址是:精讀《組件值與聯(lián)動》· Issue #469 · dt-fe/weekly
以上就是前端可視化搭建組件值與聯(lián)動實現(xiàn)詳解的詳細內(nèi)容,更多關于前端可視化搭建組件值聯(lián)動的資料請關注腳本之家其它相關文章!
相關文章
總結(jié)JavaScript中BigIn函數(shù)常見的屬性
本文基于JavaScript基礎,介紹了 BigInt 函數(shù),常見的屬性,通過 BigInt 函數(shù)進行數(shù)字運算符的比較。布爾運算等等,通過按案例的分析進行詳細的講解,需要的朋友可以參考一下2021-10-10
mini?webpack打包基礎解決包緩存和環(huán)依賴
這篇文章主要為大家介紹了mini?webpack打包基礎解決包緩存和環(huán)依賴示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-09-09

