前端可視化搭建組件值與聯(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)文章
TS?項(xiàng)目中高效處理接口返回?cái)?shù)據(jù)方法詳解
這篇文章主要為大家介紹了TS?項(xiàng)目中如何高效的處理接口返回的數(shù)據(jù)的方法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01微信小程序滾動(dòng)Tab實(shí)現(xiàn)左右可滑動(dòng)切換
這篇文章主要介紹了微信小程序滾動(dòng)Tab實(shí)現(xiàn)左右可滑動(dòng)切換的相關(guān)資料,這里提供實(shí)現(xiàn)實(shí)例幫助大家實(shí)現(xiàn)這樣的功能,需要的朋友可以參考下2017-08-08JavaScript數(shù)組對(duì)象高階函數(shù)reduce的妙用詳解
這篇文章主要為大家介紹了JavaScript數(shù)組對(duì)象高階函數(shù)reduce的妙用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-04-04一文詳解typeScript的extends關(guān)鍵字
這篇文章主要為大家介紹了typeScript的extends關(guān)鍵字使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03tree?shaking對(duì)打包體積優(yōu)化及作用
這篇文章主要為大家介紹了tree?shaking對(duì)打包體積優(yōu)化及作用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-07-07Openlayer?add?mark及添加hover效果實(shí)例詳解
這篇文章主要為大家介紹了Openlayer?add?mark及添加hover效果實(shí)例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-11-11微信小程序 setData使用方法及常用錯(cuò)誤解決辦法
這篇文章主要介紹了微信小程序 setData使用方法及常用錯(cuò)誤解決辦法的相關(guān)資料,需要的朋友可以參考下2017-05-05