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

