摘要:它每一行代碼都凝結(jié)著我從深坑中跳出來(lái)之后的思考,是下文介紹了所有問(wèn)題和場(chǎng)景的解決方案。在版本推出了新的,這也是所官方推薦的一種跨傳遞數(shù)據(jù)的解決方案。
干貨高能預(yù)警,此文章信息量巨大,大部分內(nèi)容為對(duì)現(xiàn)狀問(wèn)題的思考和現(xiàn)有技術(shù)的論證。 感興趣的朋友可以先收藏,然后慢慢研讀。此文凝結(jié)了我在中臺(tái)領(lǐng)域所有的思考和探索,相信讀完此文,能夠讓你對(duì)中臺(tái)領(lǐng)域的常見(jiàn)業(yè)務(wù)場(chǎng)景和解決方法有著全新的認(rèn)知。
此文轉(zhuǎn)載請(qǐng)注明出處。
在2019年5月11日的那個(gè)周末,我在FDCon 2019大會(huì)上進(jìn)行一次有關(guān)中臺(tái)領(lǐng)域的分享,分享的標(biāo)題是《業(yè)務(wù)實(shí)現(xiàn)標(biāo)準(zhǔn)化在中臺(tái)領(lǐng)域的探索》,并在現(xiàn)場(chǎng)發(fā)布了RCRE這個(gè)庫(kù),并介紹了如何使用RCRE來(lái)解決中臺(tái)業(yè)務(wù)開(kāi)發(fā)所面臨的各種問(wèn)題。
會(huì)后看了一些同學(xué)的吐槽,可能是我分享方式的問(wèn)題,使得當(dāng)時(shí)并沒(méi)有詳細(xì)的闡述RCRE產(chǎn)生的背景和原因,以及當(dāng)時(shí)所實(shí)際面臨的痛點(diǎn),而是僅僅去介紹如何使用RCRE了,難免也被冠以出去打廣告的嫌疑。
RCRE的誕生并不是一蹴而就,而是我在這個(gè)領(lǐng)域多年摸爬滾打的精華。它每一行代碼都凝結(jié)著我從深坑中跳出來(lái)之后的思考,是下文介紹了所有問(wèn)題和場(chǎng)景的解決方案。
初次公開(kāi)分享難免會(huì)經(jīng)驗(yàn)不足,對(duì)在場(chǎng)觀(guān)眾的需求把控不清晰?,F(xiàn)場(chǎng)演示代碼,可能并不能充分體現(xiàn)出這些API產(chǎn)生的背景和原因。所以為了滿(mǎn)足當(dāng)時(shí)大家的需求,所以這篇文章,不講代碼,只講思考和論證,來(lái)介紹當(dāng)時(shí)我在中臺(tái)領(lǐng)域所面臨的問(wèn)題,以及我針對(duì)這些問(wèn)題的看法和思考和最后我為什么要在RCRE中設(shè)計(jì)這樣的功能來(lái)解決這些問(wèn)題。
更完美的狀態(tài)管理方案過(guò)去的幾年,中臺(tái)領(lǐng)域出現(xiàn)了很多非常優(yōu)質(zhì)的UI組件庫(kù),比如Ant.Design, Element-UI等,這些組件庫(kù)解決了過(guò)去前端工程師所面臨的還原設(shè)計(jì)稿成本高的問(wèn)題,通過(guò)采用統(tǒng)一的設(shè)計(jì)風(fēng)格的UI組件,就能讓前端工程師無(wú)需再專(zhuān)注于切圖和寫(xiě)CSS,而是更專(zhuān)注于頁(yè)面邏輯的實(shí)現(xiàn)。
在頁(yè)面邏輯的實(shí)現(xiàn)層面,UI組件的狀態(tài)管理也有了很大的發(fā)展。隨著Flux的提出,再到Redux,Mobx等,使用一個(gè)狀態(tài)管理庫(kù)來(lái)管理一個(gè)應(yīng)用的狀態(tài)已經(jīng)成為了前端主流,甚至在最新的React中,還會(huì)有UseReducer這樣源自Redux的API出現(xiàn)。
而對(duì)于狀態(tài)管理,社區(qū)也衍生出兩種完全不同的思路。
狀態(tài)管理的兩極分化一種是以Redux為主導(dǎo)的不可變數(shù)據(jù)流的方案,通過(guò)讓整個(gè)應(yīng)用共享一個(gè)全局的Store,并且強(qiáng)調(diào)每一次數(shù)據(jù)更新都要保證State完全不可變,以及完全避免使用對(duì)象引用賦值的方式來(lái)更新?tīng)顟B(tài)這樣的方式來(lái)保證對(duì)頁(yè)面的數(shù)據(jù)操作,讓整個(gè)應(yīng)用具備可追溯,可回滾,可調(diào)試的特性。這樣的特性在面對(duì)代碼如山一樣的大型復(fù)雜應(yīng)用,有著非同一般的優(yōu)勢(shì),能夠快速來(lái)定位和解決問(wèn)題。
不過(guò)Redux這樣的模式也存在一定的弊端,首當(dāng)其中的就是它要求開(kāi)發(fā)者要完全按照官方所描述的那樣,寫(xiě)大量的Action,Reducer這種的樣板代碼,會(huì)讓代碼行數(shù)大量膨脹,使得開(kāi)發(fā)一個(gè)小功能變得非常繁瑣。使用單一的State來(lái)管理就需要開(kāi)發(fā)者自己去完成State的結(jié)構(gòu)設(shè)計(jì),同時(shí)不可變數(shù)據(jù)狀態(tài)管理僅僅是Redux所強(qiáng)調(diào)的一種思想和要求而已,由于并沒(méi)有提供有效避免對(duì)象引用賦值的解決方案,就需要開(kāi)發(fā)者時(shí)刻遵守這種模式,以免對(duì)不可變?cè)斐善茐摹?/p>
因此Redux這種設(shè)計(jì)模式固然有效,但是過(guò)于繁瑣和強(qiáng)調(diào)模式也是它所存在的弊端。
而另外一種則是與Redux完全相反的思路,比如Mobx。它鼓勵(lì)開(kāi)發(fā)者通過(guò)對(duì)象引用賦值來(lái)更改狀態(tài)。Mobx通過(guò)給對(duì)象添加Proxy的方式,獲得了每個(gè)用戶(hù)每個(gè)React組件所依賴(lài)的屬性,這樣就拿到了對(duì)象和組件之間的屬性映射關(guān)系,這樣Mobx就能依據(jù)這些依賴(lài)關(guān)系,自動(dòng)實(shí)現(xiàn)組件的更新。使用Mobx之后,State的很多細(xì)節(jié)都交給Mobx進(jìn)行管理,也就不會(huì)有Redux那種State設(shè)計(jì)的工作了,同時(shí)也就不存在像Redux那樣,編寫(xiě)大量的樣板代碼,而是直接修改狀態(tài)數(shù)據(jù)就能達(dá)到預(yù)期的效果。
Mobx這種的思想和Vue的機(jī)制非常類(lèi)似,同時(shí)也都存在同樣的一個(gè)弊端——由于沒(méi)有狀態(tài)的副本,無(wú)法實(shí)現(xiàn)狀態(tài)的回滾。數(shù)據(jù)之間的關(guān)系捉摸不清,更新的實(shí)現(xiàn)完全被隱藏在Mobx內(nèi)部,對(duì)開(kāi)發(fā)者不可見(jiàn),當(dāng)狀態(tài)復(fù)雜之后,就會(huì)造成調(diào)試?yán)щy,Bug難以復(fù)現(xiàn)的問(wèn)題。
Mobx這種設(shè)計(jì)模式能在早期能極大提升開(kāi)發(fā)效率,但是在項(xiàng)目后期就會(huì)給維護(hù)和調(diào)試造成一定的困難,造成效率的降低。
可見(jiàn),在狀態(tài)管理不可變數(shù)據(jù)和可變數(shù)據(jù)都各有各的優(yōu)缺點(diǎn),貌似是魚(yú)和熊掌不可兼得。那么問(wèn)題來(lái)了,是否存在一種新的技術(shù)方案,能夠結(jié)合Redux和Mobx的優(yōu)點(diǎn)呢?
有關(guān)Redux和Mobx之間對(duì)比的詳細(xì)的內(nèi)容,可以繼續(xù)看這篇文章:www.educba.com/mobx-vs-red…
簡(jiǎn)單而又可靠的狀態(tài)管理在大型復(fù)雜應(yīng)用開(kāi)發(fā)這種場(chǎng)景下,Redux可靠但是不簡(jiǎn)單,Mobx簡(jiǎn)單而又不可靠,因此就需要找到一種簡(jiǎn)單而又可靠的狀態(tài)管理方法。
Redux的可靠在于它能夠讓狀態(tài)可回溯,可監(jiān)控,使用單一的狀態(tài)能降低模塊太多所帶來(lái)的復(fù)雜度。Mobx的簡(jiǎn)單在于它使用方便,對(duì)寫(xiě)代碼沒(méi)有太多要求,也不需要很多的代碼就能實(shí)現(xiàn)功能。
對(duì)于大型復(fù)雜應(yīng)用來(lái)說(shuō),狀態(tài)可回溯,可監(jiān)控這些特性是重中之重,有了它才能讓整個(gè)應(yīng)用不會(huì)因?yàn)樘珡?fù)雜而失控。因此優(yōu)化的方向就被轉(zhuǎn)化為:能否借鑒Mobx這種簡(jiǎn)單易用的思想,來(lái)降低Redux的使用成本。
在使用單向不可變數(shù)據(jù)流這種背景下,降低Redux的使用成本需要往以下三個(gè)方面發(fā)力:
combineReducer的使用會(huì)讓開(kāi)發(fā)更繁瑣,因此需要避免每次開(kāi)發(fā)都需要進(jìn)行State結(jié)構(gòu)設(shè)計(jì)
每一次數(shù)據(jù)操作都要寫(xiě)Action,Reducer也會(huì)讓開(kāi)發(fā)更繁瑣,因此需要避免編寫(xiě)大量的Action,Reducer
不是所有人寫(xiě)的Reducer都能保證State修改不可變,因此需要一種替代方案來(lái)修改State
針對(duì)以上三個(gè)方面,我認(rèn)為可以采取以下方法來(lái)進(jìn)行解決:
利用將組件之間的結(jié)構(gòu)關(guān)系映射到State,就能在一開(kāi)始就推斷出State的結(jié)構(gòu),進(jìn)而自動(dòng)幫助開(kāi)發(fā)者完成combineReducer這樣的操作。
將多個(gè)Action進(jìn)行合并,為開(kāi)發(fā)者直接提供通用Action的方式,多個(gè)Action之間利用參數(shù)來(lái)進(jìn)行區(qū)分,以解決Action過(guò)多的問(wèn)題。
為開(kāi)發(fā)者封裝狀態(tài)操作的API,在內(nèi)部實(shí)現(xiàn)不可變的數(shù)據(jù)操作,避免開(kāi)發(fā)者直接接觸到State。
有了上面三個(gè)基本的思想,接下來(lái)就是要思考如何才能夠和現(xiàn)有的Redux架構(gòu)進(jìn)行整合。
像Mobx一樣去使用Redux首先,第二個(gè)和第三個(gè)方法可以被整合成一個(gè)API——一個(gè)通用,保證狀態(tài)不可變的狀態(tài)修改API。這樣就和Mobx直接改了數(shù)據(jù)狀態(tài)就更新的操作很相像了——調(diào)用這個(gè)API就把修改狀態(tài)搞定了。
而對(duì)于第一點(diǎn),熟悉react-redux的同學(xué)都知道,Redux中的State,是通過(guò)編寫(xiě)mapStateToProps函數(shù)來(lái)將狀態(tài)映射到組件的Props上的。而mapStateToProps函數(shù)的參數(shù)卻是整個(gè)Redux的State,想要將它映射到組件中,還需要完成從State取值的操作。而當(dāng)我們?cè)谝婚_(kāi)始設(shè)計(jì)狀態(tài)的時(shí)候,依然需要去想個(gè)名字來(lái)完成整個(gè)狀態(tài)的結(jié)構(gòu)設(shè)計(jì),前后一對(duì)比,仔細(xì)想想后會(huì)發(fā)現(xiàn),這一前一后都是需要一個(gè)Key才能完成,為何不用同一個(gè)Key呢?這樣一個(gè)Key既可以完成Redux的State中,每一個(gè)Reducer的劃分,也可以完成mapStateToProps的時(shí)候,屬性的讀取。
所以我們只需要將這個(gè)的一個(gè)Key放到一個(gè)組件的屬性上,通過(guò)組件的掛載來(lái)完成過(guò)去需要combineReducer才能完成的狀態(tài)劃分,然后再mapStateToProps的時(shí)候,同樣依據(jù)這個(gè)屬性,完成State到Props的映射。而且通過(guò)這樣的方式,整個(gè)State的結(jié)構(gòu)都完全可以在組件上進(jìn)行控制了,也就不需要再去使用combineReducer這樣的API了。
通過(guò)上述的講述的方法,我們就可以將它們封裝起來(lái),做成一個(gè)React組件,讓這個(gè)組件來(lái)幫助我們管理狀態(tài),并且通過(guò)這個(gè)組件的API來(lái)修改狀態(tài)。這也就是RCRE中,Container組件背后的思想。
Mobx的簡(jiǎn)單不光光在于開(kāi)發(fā)者不需要思考如何去更新和管理狀態(tài),它還有一個(gè)很大的優(yōu)勢(shì)在于,你可以再任何一個(gè)地方都可以直接去修改狀態(tài)。相比目前Redux中,一些值和函數(shù)都采用props進(jìn)行傳遞這種繁瑣的方式,Mobx這樣的功能會(huì)讓人感覺(jué)方便不少。
因此,即使現(xiàn)在有了Container這種可以幫助我們自動(dòng)管理狀態(tài)的組件之外,我們還需要一種類(lèi)似于Mobx這樣,可以繞過(guò)props也能傳遞數(shù)據(jù)和方法的設(shè)計(jì)。
React在16版本推出了新的Context API,這也是所官方推薦的一種跨props傳遞數(shù)據(jù)的解決方案。因此我們可以利用這個(gè)API,來(lái)實(shí)現(xiàn)在Container組件內(nèi)部的任何一個(gè)地方,都可以自由讀取狀態(tài)和修改狀態(tài)。也就是RCRE中,ES組件背后的思想。
總結(jié)一下,解決Redux使用成本高的問(wèn)題的核心就在于,找出那些可以被重復(fù)利用,差異性不是特別大的地方,再加以封裝,就能得到非常不錯(cuò)的效果。
總結(jié)來(lái)看的話(huà),整個(gè)模型就可以使用下面的圖來(lái)進(jìn)行概括。
解決組件聯(lián)動(dòng)所帶來(lái)的復(fù)雜性
寫(xiě)過(guò)中臺(tái)類(lèi)型系統(tǒng)的人都知道,凡是涉及到組件聯(lián)動(dòng)的需求,項(xiàng)目排期一定很長(zhǎng)。因?yàn)橐坏╉?yè)面中的組件有了關(guān)系,那么就得花費(fèi)大量時(shí)間來(lái)處理每一次聯(lián)動(dòng)背后,每個(gè)組件的更新,組件的數(shù)據(jù)狀態(tài)創(chuàng)建與銷(xiāo)毀,稍有不注意,就有可能寫(xiě)出聯(lián)動(dòng)之后組件沒(méi)有正確更新,或者是組件銷(xiāo)毀數(shù)據(jù)沒(méi)有銷(xiāo)毀的Bug。
當(dāng)每天維護(hù)的就是這樣一個(gè)包含數(shù)不清的聯(lián)動(dòng)關(guān)系的大型系統(tǒng),每一個(gè)Bug所帶來(lái)的損失都不可估量的時(shí)候,這背后的難度也就可想而知了。
組件聯(lián)動(dòng)的本質(zhì)組件聯(lián)動(dòng)本身并不復(fù)雜,我們可以把它簡(jiǎn)單描述為:當(dāng)一個(gè)組件更新之后修改全局的狀態(tài),其他組件需要根據(jù)狀態(tài)來(lái)做出相應(yīng)的反應(yīng)。同時(shí)組件修改狀態(tài)并不一定是同步操作,它還有可能是異步的操作,比如調(diào)用一個(gè)接口。組件修改狀態(tài)它僅僅是一個(gè)單向的操作,是很容易被理解的,而大家都覺(jué)得開(kāi)發(fā)帶有組件聯(lián)動(dòng)的功能很復(fù)雜的原因是在于,當(dāng)這個(gè)組件完成了狀態(tài)更新之后,究竟有哪些組件會(huì)因此而聯(lián)動(dòng),將是一件很復(fù)雜的事情。
單向數(shù)據(jù)流思想的價(jià)值所在在過(guò)去MVC架構(gòu)的應(yīng)用中,這樣的場(chǎng)景是非常難以處理的。因?yàn)榻M件與組件之間的通信是通過(guò)發(fā)布訂閱這種模式進(jìn)行的,當(dāng)組件之間關(guān)系復(fù)雜之后,就會(huì)形成一種網(wǎng)狀的依賴(lài)結(jié)構(gòu),在這種結(jié)構(gòu)下,暫且不說(shuō)能不能理清它們之間的關(guān)系,光是可能出現(xiàn)的環(huán)形依賴(lài)所造成的死循環(huán),就已經(jīng)讓開(kāi)發(fā)者抓狂。
React的單向數(shù)據(jù)流思想,我認(rèn)為就是應(yīng)對(duì)這種問(wèn)題最好的方法。因?yàn)樵趩蜗驍?shù)據(jù)流的架構(gòu)下,組件之間的關(guān)系從過(guò)去的網(wǎng)狀結(jié)構(gòu),轉(zhuǎn)變成了樹(shù)狀結(jié)構(gòu)。在樹(shù)狀結(jié)構(gòu)模型下,組件與組件之間只存在,父子關(guān)系與兄弟關(guān)系這兩種情況,而且還沒(méi)有環(huán)形依賴(lài)。這就大大簡(jiǎn)化了關(guān)系復(fù)雜所產(chǎn)生的一系列問(wèn)題,讓整個(gè)組件結(jié)構(gòu)一直都能保持穩(wěn)定。
每個(gè)組件都要管好自己
接下來(lái)就是要思考,當(dāng)一個(gè)組件更新的時(shí)候,該如何去更新其他組件了。
當(dāng)場(chǎng)景很復(fù)雜的時(shí)候,我們是很難搞清楚一個(gè)組件的更新究竟要觸發(fā)哪些組件,那么最好的辦法就是讓每個(gè)組件自己主動(dòng)對(duì)當(dāng)前的情況做出反應(yīng)。
這也就是不難理解,React為每個(gè)組件都提供了生命周期函數(shù)這樣的功能了。當(dāng)組件開(kāi)始聯(lián)動(dòng)的時(shí)候,我們不需要分析出一個(gè)組件究竟需要影響哪些組件,而是讓每一個(gè)組件都管好自己就好了,就像父母和老師經(jīng)常就對(duì)孩子說(shuō),管好你自己,你已經(jīng)是個(gè)大人了。
通過(guò)一個(gè)組件的觸發(fā),來(lái)帶動(dòng)組件父級(jí)的更新,父級(jí)再進(jìn)而帶動(dòng)其所有組件的更新,然后每個(gè)子組件更新的時(shí)候去檢查數(shù)據(jù)并作出相應(yīng)的反應(yīng),就能以可持續(xù)的方式來(lái)實(shí)現(xiàn)組件聯(lián)動(dòng)。
通過(guò)結(jié)合生命周期和組件狀態(tài)來(lái)提升效率當(dāng)組件被其他組件所影響的時(shí)候,組件大致分為三種不同的狀態(tài):
組件掛載
組件更新
組件銷(xiāo)毀
一個(gè)完備的業(yè)務(wù)組件,想要去支持被其他組件聯(lián)動(dòng)觸發(fā)的話(huà),除了組件的基礎(chǔ)渲染結(jié)構(gòu),還是需要在以上三個(gè)方面添加針對(duì)這個(gè)組件的一些實(shí)現(xiàn)。不過(guò)當(dāng)系統(tǒng)中有很多很多的組件的時(shí)候,反復(fù)為每個(gè)組件都實(shí)現(xiàn)上訴三個(gè)方面的功能,就顯得有些重復(fù)性勞動(dòng)。
因此我們就需要想個(gè)辦法不去多帶帶為每個(gè)組件都編寫(xiě)這些邏輯,而是尋找到一種更為通用的方法。首先,我們需要先對(duì)這三個(gè)方面的功能進(jìn)行更為細(xì)致的分析。
組件的掛載的時(shí)候,除了要初始化一些私有的數(shù)據(jù)和狀態(tài)之外,可能和其他組件產(chǎn)生影響的就是這個(gè)組件的默認(rèn)值了,當(dāng)組件初始化的時(shí)候,就要立刻將組件初始化寫(xiě)入到狀態(tài)中,來(lái)完成一些特定業(yè)務(wù)需求所需要的初始默認(rèn)值。
當(dāng)組件被更新的時(shí)候,如果整個(gè)組件渲染的數(shù)據(jù)完全是來(lái)自于props,是個(gè)完全的受控組件的話(huà),正常情況下是不需要做任何處理的。
當(dāng)組件被銷(xiāo)毀的時(shí),如果業(yè)務(wù)有需求,是需要自動(dòng)將這個(gè)組件所帶有的狀態(tài)也一并在狀態(tài)中刪除。
通過(guò)以上分析,可以看出,在生命周期內(nèi)所實(shí)現(xiàn)了和狀態(tài)有關(guān)的操作,都是對(duì)某個(gè)指定的Key執(zhí)行新增或者刪除相關(guān)的操作。所以要想提升效率,就只需要將這個(gè)Key也作為組件的一個(gè)屬性,然后就可以在底層實(shí)現(xiàn)通用的掛載邏輯和銷(xiāo)毀邏輯,實(shí)現(xiàn)簡(jiǎn)單的配置就完成了生命周期和組件狀態(tài)的整合。
這些思考,都可以在RCRE中的ES組件中找到對(duì)應(yīng)的實(shí)現(xiàn):
執(zhí)行狀態(tài)操作的Key: name屬性
組件初始化的默認(rèn)值: defaultValue屬性
控制組件是否需要銷(xiāo)毀時(shí)自動(dòng)清除數(shù)據(jù): clearWhenDestory屬性
接口調(diào)用在常規(guī)的中臺(tái)應(yīng)用中很常見(jiàn),任何涉及增刪改查的應(yīng)用都是需要依賴(lài)一些后端接口。
在一些簡(jiǎn)單的場(chǎng)景,你可能只需要在某個(gè)回調(diào)函數(shù)內(nèi)調(diào)用fetch就能拿到接口的數(shù)據(jù)。
不過(guò)對(duì)于較為復(fù)雜的場(chǎng)景和中大型應(yīng)用,接口的調(diào)用就更需要規(guī)范化。因此才會(huì)有利用Action來(lái)調(diào)用接口的方案出現(xiàn)。不過(guò)當(dāng)場(chǎng)景越來(lái)越復(fù)雜,比如一個(gè)Action調(diào)用多個(gè)接口這種情況,redux-thunk這種簡(jiǎn)單的方案就會(huì)顯得力不從心,因此社區(qū)又出現(xiàn)了redux-saga這種可以支持多接口并行調(diào)用等更高級(jí)的庫(kù)出現(xiàn)。不過(guò)redux-saga的學(xué)習(xí)成本并不低,甚至關(guān)于什么是saga,還專(zhuān)門(mén)有一篇論文來(lái)解釋?zhuān)馁M(fèi)這么多精力來(lái)學(xué)習(xí)各種庫(kù)和概念,等真正要在業(yè)務(wù)中實(shí)際應(yīng)用的時(shí)候,還是一頭霧水。沒(méi)有任何開(kāi)發(fā)經(jīng)驗(yàn)的同學(xué),依然很難處理好如何調(diào)用接口這個(gè)問(wèn)題。
因此關(guān)于異步獲取數(shù)據(jù),我認(rèn)為需要用一種更為簡(jiǎn)單傻瓜的設(shè)計(jì),提供一種能夠覆蓋多種業(yè)務(wù)場(chǎng)景的統(tǒng)一方法,來(lái)幫助開(kāi)發(fā)者快速理解并完成它們需要的功能。
和接口相關(guān)的常見(jiàn)業(yè)務(wù)場(chǎng)景針對(duì)這樣的問(wèn)題,從業(yè)務(wù)角度來(lái)進(jìn)行思考是一個(gè)非常不錯(cuò)的方向,在這個(gè)方向努力,就能實(shí)現(xiàn)快速解決業(yè)務(wù)中那些常見(jiàn)場(chǎng)景下的功能需求。
首先,需要來(lái)分析一下,在中臺(tái)系統(tǒng)中,和異步獲取數(shù)據(jù)相關(guān)的一些常見(jiàn)功能:
由各種參數(shù)和條件觸發(fā)的查詢(xún)
頁(yè)面一開(kāi)始初始化所需的數(shù)據(jù)
組件聯(lián)動(dòng)時(shí)需要的數(shù)據(jù)
并行調(diào)用無(wú)依賴(lài)的接口
串行調(diào)用相互依賴(lài)的接口
以上的三個(gè)方面,幾乎就囊括了常規(guī)那些中臺(tái)業(yè)務(wù)需求中除了表單驗(yàn)證之外需要接口的場(chǎng)景了。接下來(lái),就是要從這些功能中,找出它們的共同點(diǎn),這樣才能做出更為通用的設(shè)計(jì),來(lái)應(yīng)對(duì)不同需求變更所帶來(lái)的不確定性。
接口參數(shù)和接口觸發(fā)的關(guān)系對(duì)于不一樣的業(yè)務(wù)功能,接口參數(shù)和組件的觸發(fā)的時(shí)機(jī)是可變的,它取決于當(dāng)前業(yè)務(wù)所需要的字段和每個(gè)UI組件所觸發(fā)的回調(diào)函數(shù)。不變的是每一次接口的請(qǐng)求,都將伴隨著組件的更新,畢竟拿到接口數(shù)據(jù)之后,必然要更新組件才能將接口數(shù)據(jù)傳遞給其他組件。
因此對(duì)于第一類(lèi)功能,不管頁(yè)面中的組件是如何變化,只要這個(gè)組件能夠觸發(fā)接口,那么它必然會(huì)影響到接口請(qǐng)求的參數(shù),否則沒(méi)有參數(shù)變更的請(qǐng)求是不會(huì)滿(mǎn)足與當(dāng)前的業(yè)務(wù)需求的。因此關(guān)鍵點(diǎn)就在于參數(shù)的變更和請(qǐng)求接口之間的關(guān)系:
參數(shù)變化,觸發(fā)接口 參數(shù)不變,不觸發(fā)接口
恰好的是,任何狀態(tài)的更新都將觸發(fā)容器組件的更新,進(jìn)而更新整個(gè)應(yīng)用的組件。因此我們可以利用這樣的一個(gè)特性 —— 在容器組件上掛載鉤子來(lái)自動(dòng)觸發(fā)接口,并且在請(qǐng)求之前,讀取最新的狀態(tài)來(lái)動(dòng)態(tài)的去計(jì)算接口的參數(shù),進(jìn)而判斷出是否需要觸發(fā)接口。
因此我們就可以很巧妙的設(shè)計(jì)出這樣的一套觸發(fā)流程:
各種不同的操作更新了狀態(tài) --> 容器組件更新 --> 重新計(jì)算接口參數(shù) --> 判定并觸發(fā)接口接口初始化多樣性所帶來(lái)的問(wèn)題
對(duì)于第二類(lèi)功能,在最簡(jiǎn)單的情況下,頁(yè)面初始化的時(shí)候,它所依賴(lài)的接口是無(wú)條件觸發(fā)的。但是現(xiàn)實(shí)并不是如此,因?yàn)槟承┙涌诘某跏蓟谴嬖跅l件的,它可能是依賴(lài)某個(gè)組件的數(shù)據(jù),也有可能是依賴(lài)某個(gè)接口。
不過(guò)在日常業(yè)務(wù)開(kāi)發(fā)中,只有最簡(jiǎn)單的場(chǎng)景下,接口的調(diào)用是放置在componentDidMount這類(lèi)生命周期內(nèi)部,其他帶有條件的接口初始化調(diào)用,是無(wú)法放置在componentDidMount內(nèi)部的,而是分散在其他地方。壞的情況就是被放置在某個(gè)組件的回調(diào)函數(shù)內(nèi),等接口調(diào)用完再執(zhí)行下一次操作,好的情況就是會(huì)封裝一個(gè)Redux middleware, 通過(guò)全局?jǐn)r截的方法來(lái)調(diào)用。
仔細(xì)想想的話(huà),就會(huì)發(fā)現(xiàn)這樣的做法會(huì)有很多弊端,第一點(diǎn)是接口的調(diào)用不夠集中,它是分散的,這樣就會(huì)給大型應(yīng)用的代碼管理造成很大的障礙。第二點(diǎn)是接口的調(diào)用都需要一個(gè)特定的前置條件,這樣的前置條件可能是取決于代碼調(diào)用的位置,也有可能是來(lái)自于一大堆if else的判斷,這些都對(duì)如何管理和組織接口造成了很大的難題。
不過(guò)如果我們將視野放寬,從關(guān)注如何去調(diào)用一個(gè)接口,放大到組件的狀態(tài)和接口之間的關(guān)系,就會(huì)發(fā)現(xiàn)此類(lèi)問(wèn)題,都能使用上面所推導(dǎo)出的觸發(fā)流程來(lái)解決。
通過(guò)將能夠觸發(fā)接口請(qǐng)求的數(shù)據(jù)都存入到State中,并且在每個(gè)接口上添加一些觸發(fā)的附加條件,就能復(fù)用上面那個(gè)觸發(fā)流程模型:
普通組件掛載 --> 組件初始化數(shù)據(jù) --> 狀態(tài)更新 --> 容器組件更新 --> 接口判定是否滿(mǎn)足請(qǐng)求條件 --> 重新計(jì)算接口參數(shù) --> 判定并觸發(fā)接口
這樣的話(huà),我們就可以使用同一種機(jī)制和模型,來(lái)完成第一類(lèi)和第二類(lèi)場(chǎng)景下有關(guān)接口的需求。
復(fù)雜的組件聯(lián)動(dòng)所造成的開(kāi)發(fā)成本劇增組件聯(lián)動(dòng)是中臺(tái)領(lǐng)域中一個(gè)比較復(fù)雜的場(chǎng)景了,因?yàn)樗婕耙粋€(gè)組件的數(shù)據(jù)變更對(duì)其他組件的狀態(tài)影響。
當(dāng)頁(yè)面中一個(gè)組件的數(shù)據(jù)發(fā)生了變更,如果有一些組件和這個(gè)組件存在聯(lián)動(dòng)的話(huà),那么所有涉及的組件都將所有反應(yīng),反應(yīng)的行為通常包括新組件的掛載,現(xiàn)有組件的更新,以及組件的銷(xiāo)毀等。組件之間的聯(lián)動(dòng)關(guān)系并不是固定的,而是完全取決于當(dāng)前的業(yè)務(wù)邏輯。如果在如此復(fù)雜的組件關(guān)系中,還需要去調(diào)用新的接口,例如需要請(qǐng)求接口來(lái)為新出現(xiàn)的下拉選項(xiàng)組件提供數(shù)據(jù),那么在何處調(diào)用這個(gè)接口,就又是一個(gè)值得推敲的問(wèn)題了。
組件聯(lián)動(dòng)之后去調(diào)用接口,并不是僅僅在新組件的componentDidMount中寫(xiě)入接口調(diào)用那么簡(jiǎn)單,因?yàn)檫@個(gè)接口調(diào)用,不一定是在當(dāng)前組件掛載完畢之后就滿(mǎn)足請(qǐng)求的條件,有可能新的接口調(diào)用,是需要兩個(gè)以上的接口都完成掛載并初始化數(shù)據(jù)之后才能發(fā)起請(qǐng)求。這樣的話(huà),接口的調(diào)用就只能被移植到狀態(tài)更新之后,然后再多帶帶編寫(xiě)一些判定才能解決。
從此可見(jiàn),組件的聯(lián)動(dòng)和特定的接口觸發(fā)條件會(huì)急劇增大完成需求的難度。如果我們將上面所介紹的機(jī)制拿來(lái)和現(xiàn)有的場(chǎng)景進(jìn)行對(duì)比后發(fā)現(xiàn),組件的聯(lián)動(dòng)帶來(lái)的接口觸發(fā),也只不過(guò)是個(gè)紙老虎。
組件的聯(lián)動(dòng),必然會(huì)涉及狀態(tài)。不管是一對(duì)一的聯(lián)動(dòng),還是一對(duì)多的聯(lián)動(dòng),都離不開(kāi)背后對(duì)組件狀態(tài)的修改。狀態(tài)能夠時(shí)刻反映出當(dāng)前組件的情況。
因?yàn)榻M件的聯(lián)動(dòng)只不過(guò)是多個(gè)組件狀態(tài)的變更,所以我們依然可以采用上面所介紹的模型來(lái)解決這樣的一類(lèi)問(wèn)題:
A組件被觸發(fā) --> 狀態(tài)更新 --> B組件和C組件做出反應(yīng) --> 狀態(tài)更新 --> 容器組件更新 --> 接口判定是否滿(mǎn)足請(qǐng)求條件 --> 重新計(jì)算接口參數(shù) --> 判定并觸發(fā)接口
這樣的話(huà),我們就可以使用同一種機(jī)制和模型,完成一二三類(lèi)場(chǎng)景下有關(guān)接口的需求。
如何處理接口之間的關(guān)系當(dāng)應(yīng)用復(fù)雜起來(lái)之后,不光組件之間存在很多的關(guān)系,接口與接口之間也是。而每一個(gè)接口的請(qǐng)求,都是需要消耗一定的網(wǎng)絡(luò)時(shí)間。但是接口與接口是否存在關(guān)聯(lián),是完全取決于當(dāng)前的業(yè)務(wù)需求和數(shù)據(jù)現(xiàn)狀。當(dāng)接口觸發(fā)的條件并不是來(lái)自于其他接口返回的數(shù)據(jù),我們可以認(rèn)為接口與接口之間不存在關(guān)聯(lián)。
如果不使用任何async await或者是redux-saga這樣的工具的話(huà),在一個(gè)函數(shù)內(nèi)調(diào)用多個(gè)接口很容易出現(xiàn)callback hell的情況,也給接口的管理造成一定的負(fù)擔(dān)。
但是,如果我們仔細(xì)研究的話(huà),會(huì)發(fā)現(xiàn)每個(gè)接口在最后,都會(huì)將返回?cái)?shù)據(jù)或者一部分寫(xiě)入到狀態(tài)中。那么如果我們給每一個(gè)接口進(jìn)行命名,讓接口返回之后,將返回?cái)?shù)據(jù)寫(xiě)入到這個(gè)名字為Key的值中。那么就可以直接在狀態(tài)中通過(guò)判定這個(gè)名字是否存在來(lái)判定接口已經(jīng)成功返回,這樣就和判斷其他組件的值是否在狀態(tài)中沒(méi)有任何區(qū)別。
有了以上的基礎(chǔ),那么判定接口是否返回就和判定組件一樣簡(jiǎn)單,因此就可以將它囊括到接口判定是否滿(mǎn)足條件中去。
總結(jié)要想將調(diào)用接口這么復(fù)雜的事情做到傻瓜化,就需要找出不同場(chǎng)景下,這些操作的共同點(diǎn),找出共同點(diǎn)就能設(shè)計(jì)一個(gè)通用的模型來(lái)解決一系列的問(wèn)題,實(shí)現(xiàn)應(yīng)對(duì)多種不同場(chǎng)景下的接口需求。這也是RCRE中Container組件的DataProvider功能的背后的思想。
流程式任務(wù)管理在中臺(tái)系統(tǒng)中,還有一類(lèi)特殊的業(yè)務(wù)功能是很難被一種模型所概括的——由用戶(hù)行為所觸發(fā)了線(xiàn)性交互邏輯。
這種類(lèi)型的業(yè)務(wù)功能有一些比較明顯的特點(diǎn):
它并不復(fù)雜,通常是一連串操作的組合
它由用戶(hù)行為所觸發(fā),也可能會(huì)涉及一些連續(xù)交互的功能。
完全由業(yè)務(wù)邏輯所主導(dǎo),并沒(méi)有太多的共同點(diǎn)
通常情況下,這類(lèi)邏輯就大量分散在系統(tǒng)的各個(gè)組件內(nèi)部,看上去像是某些事件的回調(diào)函數(shù)。但是隨著需求的不斷迭代,就會(huì)讓組件變得非常膨脹,以至于會(huì)影響整個(gè)組件的可維護(hù)性。
由于每個(gè)功能都是完全按照需求所定制化開(kāi)發(fā)的,在一些常見(jiàn)業(yè)務(wù)功能都被高度封裝的情況下,多個(gè)功能之間的銜接,依然需要工程師人工編寫(xiě)代碼來(lái)進(jìn)行完成。
這樣就會(huì)造成一個(gè)問(wèn)題——功能的復(fù)用程度并不是特別高,因?yàn)橛邢喈?dāng)一部分的代碼都是膠水代碼,是無(wú)法被復(fù)用的。所以想要提升整體的代碼復(fù)用性,就需要去思考,如何才能減少膠水代碼的開(kāi)發(fā)。
分析交互邏輯的內(nèi)部細(xì)節(jié)倘若仔細(xì)去分析之后就會(huì)發(fā)現(xiàn),組合通用邏輯的膠水代碼,不管是執(zhí)行同步的操作還是異步的操作,它都是以線(xiàn)性的方式去執(zhí)行,相比組件與組件之間的關(guān)系來(lái)說(shuō),交互邏輯這類(lèi)代碼的結(jié)構(gòu)都比較簡(jiǎn)單,它們都是在上一個(gè)操作完成之后才能去執(zhí)行下一個(gè)操作,當(dāng)中間遇到了一些執(zhí)行錯(cuò)誤或者異常時(shí),都是退出這個(gè)操作就結(jié)束了。
task1 --> task2 --> task3 --> task4
所以,這個(gè)問(wèn)題就可以被轉(zhuǎn)變?yōu)槿绾握业揭环N能夠去結(jié)構(gòu)化同步或者異步操作的機(jī)制。
多個(gè)異步操作可以使用Promise進(jìn)行串行調(diào)用,同步的操作也可以被包裝成立刻返回的異步操作。所以可以使用Promise來(lái)將異步和同步之間的差異進(jìn)行打平。
串行調(diào)用在程序的世界中是非常常見(jiàn)的操作,例如reduce函數(shù),就是一個(gè)非常好的例子。如果能夠?qū)⒚恳粋€(gè)操作的調(diào)用,放置在一個(gè)數(shù)組中,那么就可以使用一次調(diào)用,來(lái)進(jìn)行批處理操作。
批處理的數(shù)據(jù)來(lái)源對(duì)于每個(gè)交互邏輯來(lái)說(shuō),它都需要讀取一些參數(shù)來(lái)完成它的工作。比如發(fā)起請(qǐng)求需要參數(shù),彈出確認(rèn)框需要提示信息,數(shù)據(jù)驗(yàn)證需要輸入數(shù)據(jù)。這些操作的數(shù)據(jù)來(lái)源有可能是來(lái)自于用戶(hù)觸發(fā)事件時(shí)的事件對(duì)象,也有可能是來(lái)自于當(dāng)前整個(gè)應(yīng)用中狀態(tài)的數(shù)據(jù),也有可能是來(lái)自于上一個(gè)操作的返回值。
所以如果要做這樣的一套批處理機(jī)制,讓每一個(gè)操作都能很順暢的運(yùn)行的話(huà),那么封裝所有來(lái)源的數(shù)據(jù)就是一件很有必要的事情了。
因此就需要在調(diào)用每一個(gè)操作所封裝的函數(shù)之前,把當(dāng)前所有的數(shù)據(jù)信息都收齊起來(lái),組裝成一個(gè)對(duì)象傳入到函數(shù)中,來(lái)滿(mǎn)足不同的業(yè)務(wù)需求所需要的數(shù)據(jù)。
每一個(gè)操作在執(zhí)行的過(guò)程中,都有可能讀取以下來(lái)源的數(shù)據(jù):
上一個(gè)操作的返回值
事件觸發(fā)的時(shí)候,傳遞的值
全局應(yīng)用的狀態(tài)
當(dāng)然,批處理還需要具備錯(cuò)誤能力——當(dāng)任何一個(gè)操作返回的異常,整個(gè)操作就會(huì)直接被終止。
配置聚合和控制中心任何零散的事物要想有組織的進(jìn)行工作,就必須要有控制中心。
在過(guò)去,處理交互邏輯是非常的分散的,即使現(xiàn)在有了類(lèi)似于reduce的批處理操作,如果它依然是散步在一些不為人知的角落,這依然無(wú)法解決分散所導(dǎo)致的混亂問(wèn)題。所以我們還需要將批處理的配置聚合在一起,并放置在最顯眼固定的位置,讓每一個(gè)人都知道想要找到這段邏輯是如何工作的,就需要看這里就夠了。
因此就需要思考,這樣的一個(gè)包含所有操作的信息的控制中心,應(yīng)該放置在哪里比較好。
頁(yè)面中的組件,都是以樹(shù)狀的結(jié)構(gòu)進(jìn)行組織的,那么不管這個(gè)頁(yè)面中組件的數(shù)量有多大,這些組件一定都會(huì)有一個(gè)最頂層的父級(jí)組件。所以這個(gè)站在金字塔最頂層的組件,就是放置控制中心的最佳選擇,怎么看起來(lái)感覺(jué)和現(xiàn)實(shí)世界中的情況差不多(笑。
而在React應(yīng)用中,直接和狀態(tài)通信的容器組件,就是聚合配置信息的組件了。也就是為什么在RCRE中,Task功能是作為Container組件的一個(gè)屬性的存在。
而流程式任務(wù)管理,正是RCRE的任務(wù)組功能背后的思想,通過(guò)這樣的一套機(jī)制,就能過(guò)去分散的交互邏輯,有跡可循,易于調(diào)整。
更便捷的表單驗(yàn)證表單一直都是中臺(tái)領(lǐng)域中開(kāi)發(fā)成本高的代表。它含有數(shù)不清的交互場(chǎng)景,也是業(yè)務(wù)需求最頻繁改動(dòng)的重災(zāi)區(qū)。
實(shí)現(xiàn)單個(gè)表單驗(yàn)證并不是很難的一件事情。表單驗(yàn)證的目的就是要去驗(yàn)證用戶(hù)輸入的組件數(shù)據(jù),通過(guò)驗(yàn)證數(shù)據(jù)的合法性來(lái)給予用戶(hù)一些反饋。因此表單驗(yàn)證就只有2個(gè)功能,第一是組件數(shù)據(jù)的改變觸發(fā)驗(yàn)證,第二是將驗(yàn)證結(jié)果反饋給用戶(hù)。
頁(yè)面中的數(shù)據(jù)是多變的,實(shí)現(xiàn)一個(gè)全面的數(shù)據(jù)驗(yàn)證功能,光在組件的onChange事件內(nèi)添加鉤子來(lái)觸發(fā)驗(yàn)證是遠(yuǎn)遠(yuǎn)不夠的,因?yàn)榻M件的數(shù)據(jù)不光來(lái)自于自己,還有可能會(huì)來(lái)自于其他組件。除此之外,針對(duì)頁(yè)面輸入框這種特殊的組件,觸發(fā)表單驗(yàn)證還有onBlur事件這樣特殊的交互。
因此實(shí)現(xiàn)數(shù)據(jù)的驗(yàn)證功能就需要圍繞三個(gè)方面來(lái)開(kāi)展,第一是onChange事件的觸發(fā),第二是組件所讀取的數(shù)據(jù)發(fā)生改變時(shí)觸發(fā),第三是onBlur事件這種特殊場(chǎng)景。
而對(duì)于頁(yè)面中的結(jié)果反饋,因?yàn)樗婕暗浇M件的渲染,所有是需要通過(guò)一個(gè)統(tǒng)一的狀態(tài)來(lái)進(jìn)行控制,這樣才能通過(guò)組件渲染到頁(yè)面上,進(jìn)而給予用戶(hù)提示。
所以總結(jié)來(lái)看,實(shí)現(xiàn)一個(gè)組件的驗(yàn)證功能不光光是一個(gè)簡(jiǎn)單的數(shù)據(jù)校驗(yàn)邏輯,而是要去完成以下的工作:
對(duì)數(shù)據(jù)的校驗(yàn)邏輯
onChange事件鉤子
onBlur事件的鉤子
組件更新時(shí)對(duì)數(shù)據(jù)變更的判斷
存儲(chǔ)表單驗(yàn)證狀態(tài)的State
展現(xiàn)錯(cuò)誤信息的組件
以上就是完成一個(gè)組件驗(yàn)證所需要的工作了,但是這并不是最煩人的地方,最讓開(kāi)發(fā)者頭疼的,是以上這些工作,每個(gè)需要被驗(yàn)證的組件都要完成,那么需要去寫(xiě)的代碼可就多了去了。
利用狀態(tài)來(lái)驅(qū)動(dòng)表單驗(yàn)證仔細(xì)觀(guān)察這些觸發(fā)表單的場(chǎng)景之后會(huì)發(fā)現(xiàn),上訴2,3,4點(diǎn)的是業(yè)務(wù)中最常見(jiàn)的應(yīng)用場(chǎng)景,同時(shí)這三點(diǎn)的背后,也和狀態(tài)的更新完全保持一致。因?yàn)闊o(wú)論是onChange事件還是onBlur事件,還是對(duì)數(shù)據(jù)變更的判斷,都是先有組件的狀態(tài)變更,再有的驗(yàn)證,因此充分利用這個(gè)特性來(lái)節(jié)省工作量,就是解決2,3,4這三類(lèi)問(wèn)題的突破點(diǎn)。
表單驗(yàn)證和組件狀態(tài)變更是同步變更的,那么只需要在組件變更的不同生命周期和回調(diào)函數(shù)內(nèi),添加觸發(fā)表單驗(yàn)證邏輯的鉤子,就能很好的讓表單也跟著組件的狀態(tài)一起變化。
表單驗(yàn)證的常見(jiàn)業(yè)務(wù)場(chǎng)景通過(guò)上的分析和方法,表單驗(yàn)證可以被狀態(tài)自動(dòng)觸發(fā),所以我們可以把通過(guò)狀態(tài)來(lái)觸發(fā)表單驗(yàn)證所有的場(chǎng)景都列舉出來(lái):
組件觸發(fā)onBlur事件來(lái)觸發(fā)驗(yàn)證
組件觸發(fā)onChange事件來(lái)觸發(fā)驗(yàn)證
通過(guò)一個(gè)接口來(lái)驗(yàn)證數(shù)據(jù)
組件被其他組件所聯(lián)動(dòng)來(lái)觸發(fā)驗(yàn)證
特殊驗(yàn)證場(chǎng)景,比如特定的驗(yàn)證邏輯
組件被刪除也要同步清空組件的驗(yàn)證狀態(tài)
同時(shí),除了和狀態(tài)之間的關(guān)系,表單還有一些它所特有的場(chǎng)景:
通過(guò)點(diǎn)擊提交按鈕,在發(fā)送請(qǐng)求之前觸發(fā)所有組件的驗(yàn)證
跳過(guò)被禁用按鈕的驗(yàn)證功能
多組件之間的驗(yàn)證相互互斥
同時(shí)表單的禁用特性,還會(huì)和組件聯(lián)動(dòng)有關(guān):通過(guò)一個(gè)組件,來(lái)控制另外一個(gè)組件的禁用屬性,進(jìn)而操作驗(yàn)證狀態(tài)。
提供表單特有場(chǎng)景下的支持根據(jù)以上的分析,表單有三個(gè)特有的場(chǎng)景需要被支持。對(duì)于第一個(gè)場(chǎng)景,點(diǎn)用戶(hù)點(diǎn)擊了提交按鈕的時(shí)候,最外層的Form組件會(huì)觸發(fā)onSubmit事件,因此可以為開(kāi)發(fā)者提供一個(gè)封裝好的回調(diào)函數(shù)給開(kāi)發(fā)者使用。在這個(gè)回調(diào)函數(shù)內(nèi)部,需要去依次去觸發(fā)每個(gè)組件的驗(yàn)證功能,來(lái)進(jìn)行全局的校驗(yàn),來(lái)確保提交的時(shí)候,每一項(xiàng)都驗(yàn)證通過(guò)。
在表單中,被禁用的組件是不需要驗(yàn)證功能的,因?yàn)橛脩?hù)無(wú)法更改組件的輸入,那么驗(yàn)證也就沒(méi)有了意義,因此還需要專(zhuān)門(mén)監(jiān)控組件的disabled屬性以便當(dāng)組件被設(shè)置為禁用的時(shí)候,立刻充值組件的驗(yàn)證狀態(tài)。
對(duì)于組件驗(yàn)證互斥這種特殊的驗(yàn)證邏輯,我們可以將它看作是一種將組件狀態(tài)和驗(yàn)證狀態(tài)進(jìn)行整合的功能。因?yàn)橐雽?shí)現(xiàn)驗(yàn)證互斥,就必須要去讀取其他組件的驗(yàn)證狀態(tài),并將自身取反,因此就只需要給開(kāi)發(fā)提供一個(gè)可以自定義擴(kuò)展驗(yàn)證的功能就足以,具體的專(zhuān)門(mén)邏輯實(shí)現(xiàn)交給開(kāi)發(fā)者處理。不過(guò)這里需要注意的是,在提供自定義驗(yàn)證的同時(shí),還要給開(kāi)發(fā)者提供讀取全局狀態(tài)的能力,因?yàn)閷?shí)現(xiàn)這種功能不僅要讀取自身的數(shù)據(jù),而是要讀取來(lái)自其他組件的數(shù)據(jù),這是一個(gè)需要注意的地方。
在RCRE中,組件都已經(jīng)完全具備此類(lèi)功能,能夠自動(dòng)幫助開(kāi)發(fā)者完成那些由各種狀態(tài)變更而觸發(fā)的表單驗(yàn)證場(chǎng)景。
表單自身的私有狀態(tài)由于表單也需要來(lái)存儲(chǔ)當(dāng)前的驗(yàn)證信息和錯(cuò)誤信息,因此表單也需要和組件的一樣,需要持有一些狀態(tài)。
因此想要節(jié)省開(kāi)發(fā)表單時(shí),驗(yàn)證和錯(cuò)誤信息的開(kāi)發(fā)工作量,就需要為開(kāi)發(fā)者提供一個(gè)通用的狀態(tài)存儲(chǔ)功能。同時(shí)表單的狀態(tài)并不是類(lèi)似于組件的狀態(tài)那種,會(huì)有聯(lián)動(dòng)的功能,每個(gè)組件的驗(yàn)證都是相互獨(dú)立,只為當(dāng)前組件所負(fù)責(zé)。
因此就可以直接使用React State這種輕量級(jí)的狀態(tài)管理功能來(lái)完成組件驗(yàn)證狀態(tài)的持有,通過(guò)將其封裝成一個(gè)React組件,就能方面開(kāi)發(fā)者進(jìn)行使用,這也就是RCRE中RCREForm />組件背后的思想。
除了一個(gè)存儲(chǔ)整個(gè)表單狀態(tài)的組件,每個(gè)組件的驗(yàn)證狀態(tài)還需要實(shí)時(shí)同步到這樣的統(tǒng)一存儲(chǔ)區(qū)域。因此就需要像上文所介紹的和組件通訊的機(jī)制類(lèi)似,采用React Context API來(lái)實(shí)現(xiàn)組件驗(yàn)證狀態(tài)和之間的通訊,以完成表單驗(yàn)證狀態(tài)的同步,這就是RCRE中組件背后的思想。
有了這兩個(gè)機(jī)制,開(kāi)發(fā)者就不需要手動(dòng)去編寫(xiě)實(shí)現(xiàn)來(lái)維護(hù)表單的驗(yàn)證狀態(tài)了,所以對(duì)于上述第五點(diǎn)和第六點(diǎn)所帶來(lái)的重復(fù)性工作也就迎刃而解。
寫(xiě)在最后這篇文章所有的內(nèi)容,就是RCRE這個(gè)庫(kù)背后所有的設(shè)計(jì)思路和思想了,想必你看到這里也能夠理解為什么會(huì)有RCRE這樣的庫(kù)誕生了。如果有興趣想繼續(xù)了解這個(gè)項(xiàng)目,可以點(diǎn)擊下面這個(gè)鏈接:
github.com/andycall/RC…
如果有任何問(wèn)題,歡迎在下方留言,我盡可能將內(nèi)容做到更好。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/7287.html
摘要:月日晚點(diǎn),線(xiàn)上直播,中臺(tái)一種敏捷的智能業(yè)務(wù)支持方案金融科技領(lǐng)域,能解決什么問(wèn)題在宜信年的發(fā)展歷程中,圍繞普惠金融和財(cái)富管理兩大業(yè)務(wù)板塊,宜信陸續(xù)推出了宜人貸宜人財(cái)富致誠(chéng)信用博城保險(xiǎn)等多個(gè)產(chǎn)品,技術(shù)已被廣泛應(yīng)用到各產(chǎn)品的業(yè)務(wù)線(xiàn)中。 [宜信技術(shù)沙龍】是由宜信技術(shù)學(xué)院主辦的系列技術(shù)分享活動(dòng),活動(dòng)包括線(xiàn)上和線(xiàn)下兩種形式,每期技術(shù)沙龍都將邀請(qǐng)宜信及其他互聯(lián)網(wǎng)公司的技術(shù)專(zhuān)家分享來(lái)自一線(xiàn)的實(shí)踐經(jīng)驗(yàn),...
摘要:作者也樹(shù)校對(duì)染陌素材也樹(shù)英布阿里云前端技術(shù)周刊由阿里云智能商業(yè)中臺(tái)體驗(yàn)技術(shù)團(tuán)隊(duì)整理編寫(xiě)。如何在工作中快速成長(zhǎng)致工程師的個(gè)簡(jiǎn)單技巧工程師成長(zhǎng)干貨,全文提綱如下,圖片來(lái)自阿里技術(shù)公眾號(hào)關(guān)于我們我們是阿里云智能中臺(tái)體驗(yàn)技術(shù)團(tuán)隊(duì)。 作者:@也樹(shù) 校對(duì):@染陌 素材:@也樹(shù)、@英布 《阿里云前端技術(shù)周刊》由阿里云智能商業(yè)中臺(tái)體驗(yàn)技術(shù)團(tuán)隊(duì)整理編寫(xiě)。 知乎:阿里云中臺(tái)前端/全棧團(tuán)隊(duì)專(zhuān)欄 Github...
摘要:快速摧毀敵軍設(shè)施,殺傷有敵軍生力量,最小化己方傷亡。所以前端工程師在修改完樣式以后,需要反復(fù)和設(shè)計(jì)師還原度的問(wèn)題。前端工程師依照主題包和設(shè)計(jì)稿進(jìn)行前端工程開(kāi)發(fā)。前端工程師很開(kāi)心,因?yàn)椴挥萌ネ度腴_(kāi)發(fā)組件庫(kù)和調(diào)整還原度。作者: 暮塵 2019年05月11日在上海舉辦 FDCON 2019。筆者有幸受到邀請(qǐng),參與這次盛會(huì)。這篇文章就是演講內(nèi)容的文字提煉版。 淺談中臺(tái) 在開(kāi)始正文內(nèi)容之前,先簡(jiǎn)單聊聊...
閱讀 3788·2021-11-18 13:20
閱讀 2809·2021-10-15 09:40
閱讀 1965·2021-10-11 10:58
閱讀 2215·2021-09-27 13:36
閱讀 2688·2021-09-07 10:06
閱讀 1949·2021-08-11 11:21
閱讀 1485·2019-08-29 17:04
閱讀 2146·2019-08-29 14:06