成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

前端中臺系統(tǒng)常見問題剖析與解決方案

Aklman / 2626人閱讀

摘要:它每一行代碼都凝結(jié)著我從深坑中跳出來之后的思考,是下文介紹了所有問題和場景的解決方案。在版本推出了新的,這也是所官方推薦的一種跨傳遞數(shù)據(jù)的解決方案。

干貨高能預(yù)警,此文章信息量巨大,大部分內(nèi)容為對現(xiàn)狀問題的思考和現(xiàn)有技術(shù)的論證。 感興趣的朋友可以先收藏,然后慢慢研讀。此文凝結(jié)了我在中臺領(lǐng)域所有的思考和探索,相信讀完此文,能夠讓你對中臺領(lǐng)域的常見業(yè)務(wù)場景和解決方法有著全新的認(rèn)知。

此文轉(zhuǎn)載請注明出處。

在2019年5月11日的那個周末,我在FDCon 2019大會上進(jìn)行一次有關(guān)中臺領(lǐng)域的分享,分享的標(biāo)題是《業(yè)務(wù)實現(xiàn)標(biāo)準(zhǔn)化在中臺領(lǐng)域的探索》,并在現(xiàn)場發(fā)布了RCRE這個庫,并介紹了如何使用RCRE來解決中臺業(yè)務(wù)開發(fā)所面臨的各種問題。

會后看了一些同學(xué)的吐槽,可能是我分享方式的問題,使得當(dāng)時并沒有詳細(xì)的闡述RCRE產(chǎn)生的背景和原因,以及當(dāng)時所實際面臨的痛點,而是僅僅去介紹如何使用RCRE了,難免也被冠以出去打廣告的嫌疑。

RCRE的誕生并不是一蹴而就,而是我在這個領(lǐng)域多年摸爬滾打的精華。它每一行代碼都凝結(jié)著我從深坑中跳出來之后的思考,是下文介紹了所有問題和場景的解決方案。

初次公開分享難免會經(jīng)驗不足,對在場觀眾的需求把控不清晰?,F(xiàn)場演示代碼,可能并不能充分體現(xiàn)出這些API產(chǎn)生的背景和原因。所以為了滿足當(dāng)時大家的需求,所以這篇文章,不講代碼,只講思考和論證,來介紹當(dāng)時我在中臺領(lǐng)域所面臨的問題,以及我針對這些問題的看法和思考和最后我為什么要在RCRE中設(shè)計這樣的功能來解決這些問題。

更完美的狀態(tài)管理方案

過去的幾年,中臺領(lǐng)域出現(xiàn)了很多非常優(yōu)質(zhì)的UI組件庫,比如Ant.Design, Element-UI等,這些組件庫解決了過去前端工程師所面臨的還原設(shè)計稿成本高的問題,通過采用統(tǒng)一的設(shè)計風(fēng)格的UI組件,就能讓前端工程師無需再專注于切圖和寫CSS,而是更專注于頁面邏輯的實現(xiàn)。

在頁面邏輯的實現(xiàn)層面,UI組件的狀態(tài)管理也有了很大的發(fā)展。隨著Flux的提出,再到Redux,Mobx等,使用一個狀態(tài)管理庫來管理一個應(yīng)用的狀態(tài)已經(jīng)成為了前端主流,甚至在最新的React中,還會有UseReducer這樣源自Redux的API出現(xiàn)。

而對于狀態(tài)管理,社區(qū)也衍生出兩種完全不同的思路。

狀態(tài)管理的兩極分化

一種是以Redux為主導(dǎo)的不可變數(shù)據(jù)流的方案,通過讓整個應(yīng)用共享一個全局的Store,并且強調(diào)每一次數(shù)據(jù)更新都要保證State完全不可變,以及完全避免使用對象引用賦值的方式來更新狀態(tài)這樣的方式來保證對頁面的數(shù)據(jù)操作,讓整個應(yīng)用具備可追溯,可回滾,可調(diào)試的特性。這樣的特性在面對代碼如山一樣的大型復(fù)雜應(yīng)用,有著非同一般的優(yōu)勢,能夠快速來定位和解決問題。

不過Redux這樣的模式也存在一定的弊端,首當(dāng)其中的就是它要求開發(fā)者要完全按照官方所描述的那樣,寫大量的Action,Reducer這種的樣板代碼,會讓代碼行數(shù)大量膨脹,使得開發(fā)一個小功能變得非常繁瑣。使用單一的State來管理就需要開發(fā)者自己去完成State的結(jié)構(gòu)設(shè)計,同時不可變數(shù)據(jù)狀態(tài)管理僅僅是Redux所強調(diào)的一種思想和要求而已,由于并沒有提供有效避免對象引用賦值的解決方案,就需要開發(fā)者時刻遵守這種模式,以免對不可變造成破壞。

因此Redux這種設(shè)計模式固然有效,但是過于繁瑣和強調(diào)模式也是它所存在的弊端。

而另外一種則是與Redux完全相反的思路,比如Mobx。它鼓勵開發(fā)者通過對象引用賦值來更改狀態(tài)。Mobx通過給對象添加Proxy的方式,獲得了每個用戶每個React組件所依賴的屬性,這樣就拿到了對象和組件之間的屬性映射關(guān)系,這樣Mobx就能依據(jù)這些依賴關(guān)系,自動實現(xiàn)組件的更新。使用Mobx之后,State的很多細(xì)節(jié)都交給Mobx進(jìn)行管理,也就不會有Redux那種State設(shè)計的工作了,同時也就不存在像Redux那樣,編寫大量的樣板代碼,而是直接修改狀態(tài)數(shù)據(jù)就能達(dá)到預(yù)期的效果。

Mobx這種的思想和Vue的機制非常類似,同時也都存在同樣的一個弊端——由于沒有狀態(tài)的副本,無法實現(xiàn)狀態(tài)的回滾。數(shù)據(jù)之間的關(guān)系捉摸不清,更新的實現(xiàn)完全被隱藏在Mobx內(nèi)部,對開發(fā)者不可見,當(dāng)狀態(tài)復(fù)雜之后,就會造成調(diào)試?yán)щy,Bug難以復(fù)現(xiàn)的問題。

Mobx這種設(shè)計模式能在早期能極大提升開發(fā)效率,但是在項目后期就會給維護(hù)和調(diào)試造成一定的困難,造成效率的降低。

可見,在狀態(tài)管理不可變數(shù)據(jù)和可變數(shù)據(jù)都各有各的優(yōu)缺點,貌似是魚和熊掌不可兼得。那么問題來了,是否存在一種新的技術(shù)方案,能夠結(jié)合Redux和Mobx的優(yōu)點呢?

有關(guān)Redux和Mobx之間對比的詳細(xì)的內(nèi)容,可以繼續(xù)看這篇文章:www.educba.com/mobx-vs-red…

簡單而又可靠的狀態(tài)管理

在大型復(fù)雜應(yīng)用開發(fā)這種場景下,Redux可靠但是不簡單,Mobx簡單而又不可靠,因此就需要找到一種簡單而又可靠的狀態(tài)管理方法。

Redux的可靠在于它能夠讓狀態(tài)可回溯,可監(jiān)控,使用單一的狀態(tài)能降低模塊太多所帶來的復(fù)雜度。Mobx的簡單在于它使用方便,對寫代碼沒有太多要求,也不需要很多的代碼就能實現(xiàn)功能。

對于大型復(fù)雜應(yīng)用來說,狀態(tài)可回溯,可監(jiān)控這些特性是重中之重,有了它才能讓整個應(yīng)用不會因為太復(fù)雜而失控。因此優(yōu)化的方向就被轉(zhuǎn)化為:能否借鑒Mobx這種簡單易用的思想,來降低Redux的使用成本。

在使用單向不可變數(shù)據(jù)流這種背景下,降低Redux的使用成本需要往以下三個方面發(fā)力:

    combineReducer的使用會讓開發(fā)更繁瑣,因此需要避免每次開發(fā)都需要進(jìn)行State結(jié)構(gòu)設(shè)計

    每一次數(shù)據(jù)操作都要寫Action,Reducer也會讓開發(fā)更繁瑣,因此需要避免編寫大量的Action,Reducer

    不是所有人寫的Reducer都能保證State修改不可變,因此需要一種替代方案來修改State

針對以上三個方面,我認(rèn)為可以采取以下方法來進(jìn)行解決:

    利用將組件之間的結(jié)構(gòu)關(guān)系映射到State,就能在一開始就推斷出State的結(jié)構(gòu),進(jìn)而自動幫助開發(fā)者完成combineReducer這樣的操作。

    將多個Action進(jìn)行合并,為開發(fā)者直接提供通用Action的方式,多個Action之間利用參數(shù)來進(jìn)行區(qū)分,以解決Action過多的問題。

    為開發(fā)者封裝狀態(tài)操作的API,在內(nèi)部實現(xiàn)不可變的數(shù)據(jù)操作,避免開發(fā)者直接接觸到State。

有了上面三個基本的思想,接下來就是要思考如何才能夠和現(xiàn)有的Redux架構(gòu)進(jìn)行整合。

像Mobx一樣去使用Redux

首先,第二個和第三個方法可以被整合成一個API——一個通用,保證狀態(tài)不可變的狀態(tài)修改API。這樣就和Mobx直接改了數(shù)據(jù)狀態(tài)就更新的操作很相像了——調(diào)用這個API就把修改狀態(tài)搞定了。

而對于第一點,熟悉react-redux的同學(xué)都知道,Redux中的State,是通過編寫mapStateToProps函數(shù)來將狀態(tài)映射到組件的Props上的。而mapStateToProps函數(shù)的參數(shù)卻是整個Redux的State,想要將它映射到組件中,還需要完成從State取值的操作。而當(dāng)我們在一開始設(shè)計狀態(tài)的時候,依然需要去想個名字來完成整個狀態(tài)的結(jié)構(gòu)設(shè)計,前后一對比,仔細(xì)想想后會發(fā)現(xiàn),這一前一后都是需要一個Key才能完成,為何不用同一個Key呢?這樣一個Key既可以完成Redux的State中,每一個Reducer的劃分,也可以完成mapStateToProps的時候,屬性的讀取。

所以我們只需要將這個的一個Key放到一個組件的屬性上,通過組件的掛載來完成過去需要combineReducer才能完成的狀態(tài)劃分,然后再mapStateToProps的時候,同樣依據(jù)這個屬性,完成State到Props的映射。而且通過這樣的方式,整個State的結(jié)構(gòu)都完全可以在組件上進(jìn)行控制了,也就不需要再去使用combineReducer這樣的API了。

通過上述的講述的方法,我們就可以將它們封裝起來,做成一個React組件,讓這個組件來幫助我們管理狀態(tài),并且通過這個組件的API來修改狀態(tài)。這也就是RCRE中,Container組件背后的思想。

Mobx的簡單不光光在于開發(fā)者不需要思考如何去更新和管理狀態(tài),它還有一個很大的優(yōu)勢在于,你可以再任何一個地方都可以直接去修改狀態(tài)。相比目前Redux中,一些值和函數(shù)都采用props進(jìn)行傳遞這種繁瑣的方式,Mobx這樣的功能會讓人感覺方便不少。

因此,即使現(xiàn)在有了Container這種可以幫助我們自動管理狀態(tài)的組件之外,我們還需要一種類似于Mobx這樣,可以繞過props也能傳遞數(shù)據(jù)和方法的設(shè)計。

React在16版本推出了新的Context API,這也是所官方推薦的一種跨props傳遞數(shù)據(jù)的解決方案。因此我們可以利用這個API,來實現(xiàn)在Container組件內(nèi)部的任何一個地方,都可以自由讀取狀態(tài)和修改狀態(tài)。也就是RCRE中,ES組件背后的思想。

總結(jié)一下,解決Redux使用成本高的問題的核心就在于,找出那些可以被重復(fù)利用,差異性不是特別大的地方,再加以封裝,就能得到非常不錯的效果。

總結(jié)來看的話,整個模型就可以使用下面的圖來進(jìn)行概括。

解決組件聯(lián)動所帶來的復(fù)雜性

寫過中臺類型系統(tǒng)的人都知道,凡是涉及到組件聯(lián)動的需求,項目排期一定很長。因為一旦頁面中的組件有了關(guān)系,那么就得花費大量時間來處理每一次聯(lián)動背后,每個組件的更新,組件的數(shù)據(jù)狀態(tài)創(chuàng)建與銷毀,稍有不注意,就有可能寫出聯(lián)動之后組件沒有正確更新,或者是組件銷毀數(shù)據(jù)沒有銷毀的Bug。

當(dāng)每天維護(hù)的就是這樣一個包含數(shù)不清的聯(lián)動關(guān)系的大型系統(tǒng),每一個Bug所帶來的損失都不可估量的時候,這背后的難度也就可想而知了。

組件聯(lián)動的本質(zhì)

組件聯(lián)動本身并不復(fù)雜,我們可以把它簡單描述為:當(dāng)一個組件更新之后修改全局的狀態(tài),其他組件需要根據(jù)狀態(tài)來做出相應(yīng)的反應(yīng)。同時組件修改狀態(tài)并不一定是同步操作,它還有可能是異步的操作,比如調(diào)用一個接口。組件修改狀態(tài)它僅僅是一個單向的操作,是很容易被理解的,而大家都覺得開發(fā)帶有組件聯(lián)動的功能很復(fù)雜的原因是在于,當(dāng)這個組件完成了狀態(tài)更新之后,究竟有哪些組件會因此而聯(lián)動,將是一件很復(fù)雜的事情。

單向數(shù)據(jù)流思想的價值所在

在過去MVC架構(gòu)的應(yīng)用中,這樣的場景是非常難以處理的。因為組件與組件之間的通信是通過發(fā)布訂閱這種模式進(jìn)行的,當(dāng)組件之間關(guān)系復(fù)雜之后,就會形成一種網(wǎng)狀的依賴結(jié)構(gòu),在這種結(jié)構(gòu)下,暫且不說能不能理清它們之間的關(guān)系,光是可能出現(xiàn)的環(huán)形依賴所造成的死循環(huán),就已經(jīng)讓開發(fā)者抓狂。

React的單向數(shù)據(jù)流思想,我認(rèn)為就是應(yīng)對這種問題最好的方法。因為在單向數(shù)據(jù)流的架構(gòu)下,組件之間的關(guān)系從過去的網(wǎng)狀結(jié)構(gòu),轉(zhuǎn)變成了樹狀結(jié)構(gòu)。在樹狀結(jié)構(gòu)模型下,組件與組件之間只存在,父子關(guān)系與兄弟關(guān)系這兩種情況,而且還沒有環(huán)形依賴。這就大大簡化了關(guān)系復(fù)雜所產(chǎn)生的一系列問題,讓整個組件結(jié)構(gòu)一直都能保持穩(wěn)定。

每個組件都要管好自己

接下來就是要思考,當(dāng)一個組件更新的時候,該如何去更新其他組件了。

當(dāng)場景很復(fù)雜的時候,我們是很難搞清楚一個組件的更新究竟要觸發(fā)哪些組件,那么最好的辦法就是讓每個組件自己主動對當(dāng)前的情況做出反應(yīng)。

這也就是不難理解,React為每個組件都提供了生命周期函數(shù)這樣的功能了。當(dāng)組件開始聯(lián)動的時候,我們不需要分析出一個組件究竟需要影響哪些組件,而是讓每一個組件都管好自己就好了,就像父母和老師經(jīng)常就對孩子說,管好你自己,你已經(jīng)是個大人了。

通過一個組件的觸發(fā),來帶動組件父級的更新,父級再進(jìn)而帶動其所有組件的更新,然后每個子組件更新的時候去檢查數(shù)據(jù)并作出相應(yīng)的反應(yīng),就能以可持續(xù)的方式來實現(xiàn)組件聯(lián)動。

通過結(jié)合生命周期和組件狀態(tài)來提升效率

當(dāng)組件被其他組件所影響的時候,組件大致分為三種不同的狀態(tài):

    組件掛載

    組件更新

    組件銷毀

一個完備的業(yè)務(wù)組件,想要去支持被其他組件聯(lián)動觸發(fā)的話,除了組件的基礎(chǔ)渲染結(jié)構(gòu),還是需要在以上三個方面添加針對這個組件的一些實現(xiàn)。不過當(dāng)系統(tǒng)中有很多很多的組件的時候,反復(fù)為每個組件都實現(xiàn)上訴三個方面的功能,就顯得有些重復(fù)性勞動。

因此我們就需要想個辦法不去多帶帶為每個組件都編寫這些邏輯,而是尋找到一種更為通用的方法。首先,我們需要先對這三個方面的功能進(jìn)行更為細(xì)致的分析。

組件的掛載的時候,除了要初始化一些私有的數(shù)據(jù)和狀態(tài)之外,可能和其他組件產(chǎn)生影響的就是這個組件的默認(rèn)值了,當(dāng)組件初始化的時候,就要立刻將組件初始化寫入到狀態(tài)中,來完成一些特定業(yè)務(wù)需求所需要的初始默認(rèn)值。

當(dāng)組件被更新的時候,如果整個組件渲染的數(shù)據(jù)完全是來自于props,是個完全的受控組件的話,正常情況下是不需要做任何處理的。

當(dāng)組件被銷毀的時,如果業(yè)務(wù)有需求,是需要自動將這個組件所帶有的狀態(tài)也一并在狀態(tài)中刪除。

通過以上分析,可以看出,在生命周期內(nèi)所實現(xiàn)了和狀態(tài)有關(guān)的操作,都是對某個指定的Key執(zhí)行新增或者刪除相關(guān)的操作。所以要想提升效率,就只需要將這個Key也作為組件的一個屬性,然后就可以在底層實現(xiàn)通用的掛載邏輯和銷毀邏輯,實現(xiàn)簡單的配置就完成了生命周期和組件狀態(tài)的整合。

這些思考,都可以在RCRE中的ES組件中找到對應(yīng)的實現(xiàn):

    執(zhí)行狀態(tài)操作的Key: name屬性

    組件初始化的默認(rèn)值: defaultValue屬性

    控制組件是否需要銷毀時自動清除數(shù)據(jù): clearWhenDestory屬性

接口調(diào)用的通用模式

接口調(diào)用在常規(guī)的中臺應(yīng)用中很常見,任何涉及增刪改查的應(yīng)用都是需要依賴一些后端接口。

在一些簡單的場景,你可能只需要在某個回調(diào)函數(shù)內(nèi)調(diào)用fetch就能拿到接口的數(shù)據(jù)。

不過對于較為復(fù)雜的場景和中大型應(yīng)用,接口的調(diào)用就更需要規(guī)范化。因此才會有利用Action來調(diào)用接口的方案出現(xiàn)。不過當(dāng)場景越來越復(fù)雜,比如一個Action調(diào)用多個接口這種情況,redux-thunk這種簡單的方案就會顯得力不從心,因此社區(qū)又出現(xiàn)了redux-saga這種可以支持多接口并行調(diào)用等更高級的庫出現(xiàn)。不過redux-saga的學(xué)習(xí)成本并不低,甚至關(guān)于什么是saga,還專門有一篇論文來解釋,耗費這么多精力來學(xué)習(xí)各種庫和概念,等真正要在業(yè)務(wù)中實際應(yīng)用的時候,還是一頭霧水。沒有任何開發(fā)經(jīng)驗的同學(xué),依然很難處理好如何調(diào)用接口這個問題。

因此關(guān)于異步獲取數(shù)據(jù),我認(rèn)為需要用一種更為簡單傻瓜的設(shè)計,提供一種能夠覆蓋多種業(yè)務(wù)場景的統(tǒng)一方法,來幫助開發(fā)者快速理解并完成它們需要的功能。

和接口相關(guān)的常見業(yè)務(wù)場景

針對這樣的問題,從業(yè)務(wù)角度來進(jìn)行思考是一個非常不錯的方向,在這個方向努力,就能實現(xiàn)快速解決業(yè)務(wù)中那些常見場景下的功能需求。

首先,需要來分析一下,在中臺系統(tǒng)中,和異步獲取數(shù)據(jù)相關(guān)的一些常見功能:

    由各種參數(shù)和條件觸發(fā)的查詢

    頁面一開始初始化所需的數(shù)據(jù)

    組件聯(lián)動時需要的數(shù)據(jù)

    并行調(diào)用無依賴的接口

    串行調(diào)用相互依賴的接口

以上的三個方面,幾乎就囊括了常規(guī)那些中臺業(yè)務(wù)需求中除了表單驗證之外需要接口的場景了。接下來,就是要從這些功能中,找出它們的共同點,這樣才能做出更為通用的設(shè)計,來應(yīng)對不同需求變更所帶來的不確定性。

接口參數(shù)和接口觸發(fā)的關(guān)系

對于不一樣的業(yè)務(wù)功能,接口參數(shù)和組件的觸發(fā)的時機是可變的,它取決于當(dāng)前業(yè)務(wù)所需要的字段和每個UI組件所觸發(fā)的回調(diào)函數(shù)。不變的是每一次接口的請求,都將伴隨著組件的更新,畢竟拿到接口數(shù)據(jù)之后,必然要更新組件才能將接口數(shù)據(jù)傳遞給其他組件。

因此對于第一類功能,不管頁面中的組件是如何變化,只要這個組件能夠觸發(fā)接口,那么它必然會影響到接口請求的參數(shù),否則沒有參數(shù)變更的請求是不會滿足與當(dāng)前的業(yè)務(wù)需求的。因此關(guān)鍵點就在于參數(shù)的變更和請求接口之間的關(guān)系:

參數(shù)變化,觸發(fā)接口
參數(shù)不變,不觸發(fā)接口

恰好的是,任何狀態(tài)的更新都將觸發(fā)容器組件的更新,進(jìn)而更新整個應(yīng)用的組件。因此我們可以利用這樣的一個特性 —— 在容器組件上掛載鉤子來自動觸發(fā)接口,并且在請求之前,讀取最新的狀態(tài)來動態(tài)的去計算接口的參數(shù),進(jìn)而判斷出是否需要觸發(fā)接口。

因此我們就可以很巧妙的設(shè)計出這樣的一套觸發(fā)流程:

各種不同的操作更新了狀態(tài) --> 容器組件更新 --> 重新計算接口參數(shù) --> 判定并觸發(fā)接口
接口初始化多樣性所帶來的問題

對于第二類功能,在最簡單的情況下,頁面初始化的時候,它所依賴的接口是無條件觸發(fā)的。但是現(xiàn)實并不是如此,因為某些接口的初始化是存在條件的,它可能是依賴某個組件的數(shù)據(jù),也有可能是依賴某個接口。

不過在日常業(yè)務(wù)開發(fā)中,只有最簡單的場景下,接口的調(diào)用是放置在componentDidMount這類生命周期內(nèi)部,其他帶有條件的接口初始化調(diào)用,是無法放置在componentDidMount內(nèi)部的,而是分散在其他地方。壞的情況就是被放置在某個組件的回調(diào)函數(shù)內(nèi),等接口調(diào)用完再執(zhí)行下一次操作,好的情況就是會封裝一個Redux middleware, 通過全局?jǐn)r截的方法來調(diào)用。

仔細(xì)想想的話,就會發(fā)現(xiàn)這樣的做法會有很多弊端,第一點是接口的調(diào)用不夠集中,它是分散的,這樣就會給大型應(yīng)用的代碼管理造成很大的障礙。第二點是接口的調(diào)用都需要一個特定的前置條件,這樣的前置條件可能是取決于代碼調(diào)用的位置,也有可能是來自于一大堆if else的判斷,這些都對如何管理和組織接口造成了很大的難題。

不過如果我們將視野放寬,從關(guān)注如何去調(diào)用一個接口,放大到組件的狀態(tài)和接口之間的關(guān)系,就會發(fā)現(xiàn)此類問題,都能使用上面所推導(dǎo)出的觸發(fā)流程來解決。

通過將能夠觸發(fā)接口請求的數(shù)據(jù)都存入到State中,并且在每個接口上添加一些觸發(fā)的附加條件,就能復(fù)用上面那個觸發(fā)流程模型:

普通組件掛載 --> 組件初始化數(shù)據(jù) --> 狀態(tài)更新 --> 容器組件更新 --> 接口判定是否滿足請求條件 --> 重新計算接口參數(shù) --> 判定并觸發(fā)接口

這樣的話,我們就可以使用同一種機制和模型,來完成第一類和第二類場景下有關(guān)接口的需求。

復(fù)雜的組件聯(lián)動所造成的開發(fā)成本劇增

組件聯(lián)動是中臺領(lǐng)域中一個比較復(fù)雜的場景了,因為它涉及一個組件的數(shù)據(jù)變更對其他組件的狀態(tài)影響。

當(dāng)頁面中一個組件的數(shù)據(jù)發(fā)生了變更,如果有一些組件和這個組件存在聯(lián)動的話,那么所有涉及的組件都將所有反應(yīng),反應(yīng)的行為通常包括新組件的掛載,現(xiàn)有組件的更新,以及組件的銷毀等。組件之間的聯(lián)動關(guān)系并不是固定的,而是完全取決于當(dāng)前的業(yè)務(wù)邏輯。如果在如此復(fù)雜的組件關(guān)系中,還需要去調(diào)用新的接口,例如需要請求接口來為新出現(xiàn)的下拉選項組件提供數(shù)據(jù),那么在何處調(diào)用這個接口,就又是一個值得推敲的問題了。

組件聯(lián)動之后去調(diào)用接口,并不是僅僅在新組件的componentDidMount中寫入接口調(diào)用那么簡單,因為這個接口調(diào)用,不一定是在當(dāng)前組件掛載完畢之后就滿足請求的條件,有可能新的接口調(diào)用,是需要兩個以上的接口都完成掛載并初始化數(shù)據(jù)之后才能發(fā)起請求。這樣的話,接口的調(diào)用就只能被移植到狀態(tài)更新之后,然后再多帶帶編寫一些判定才能解決。

從此可見,組件的聯(lián)動和特定的接口觸發(fā)條件會急劇增大完成需求的難度。如果我們將上面所介紹的機制拿來和現(xiàn)有的場景進(jìn)行對比后發(fā)現(xiàn),組件的聯(lián)動帶來的接口觸發(fā),也只不過是個紙老虎。

組件的聯(lián)動,必然會涉及狀態(tài)。不管是一對一的聯(lián)動,還是一對多的聯(lián)動,都離不開背后對組件狀態(tài)的修改。狀態(tài)能夠時刻反映出當(dāng)前組件的情況。

因為組件的聯(lián)動只不過是多個組件狀態(tài)的變更,所以我們依然可以采用上面所介紹的模型來解決這樣的一類問題:

A組件被觸發(fā) --> 狀態(tài)更新 --> B組件和C組件做出反應(yīng) --> 狀態(tài)更新 --> 容器組件更新 --> 接口判定是否滿足請求條件 --> 重新計算接口參數(shù) --> 判定并觸發(fā)接口

這樣的話,我們就可以使用同一種機制和模型,完成一二三類場景下有關(guān)接口的需求。

如何處理接口之間的關(guān)系

當(dāng)應(yīng)用復(fù)雜起來之后,不光組件之間存在很多的關(guān)系,接口與接口之間也是。而每一個接口的請求,都是需要消耗一定的網(wǎng)絡(luò)時間。但是接口與接口是否存在關(guān)聯(lián),是完全取決于當(dāng)前的業(yè)務(wù)需求和數(shù)據(jù)現(xiàn)狀。當(dāng)接口觸發(fā)的條件并不是來自于其他接口返回的數(shù)據(jù),我們可以認(rèn)為接口與接口之間不存在關(guān)聯(lián)。

如果不使用任何async await或者是redux-saga這樣的工具的話,在一個函數(shù)內(nèi)調(diào)用多個接口很容易出現(xiàn)callback hell的情況,也給接口的管理造成一定的負(fù)擔(dān)。

但是,如果我們仔細(xì)研究的話,會發(fā)現(xiàn)每個接口在最后,都會將返回數(shù)據(jù)或者一部分寫入到狀態(tài)中。那么如果我們給每一個接口進(jìn)行命名,讓接口返回之后,將返回數(shù)據(jù)寫入到這個名字為Key的值中。那么就可以直接在狀態(tài)中通過判定這個名字是否存在來判定接口已經(jīng)成功返回,這樣就和判斷其他組件的值是否在狀態(tài)中沒有任何區(qū)別。

有了以上的基礎(chǔ),那么判定接口是否返回就和判定組件一樣簡單,因此就可以將它囊括到接口判定是否滿足條件中去。

總結(jié)

要想將調(diào)用接口這么復(fù)雜的事情做到傻瓜化,就需要找出不同場景下,這些操作的共同點,找出共同點就能設(shè)計一個通用的模型來解決一系列的問題,實現(xiàn)應(yīng)對多種不同場景下的接口需求。這也是RCRE中Container組件的DataProvider功能的背后的思想。

流程式任務(wù)管理

在中臺系統(tǒng)中,還有一類特殊的業(yè)務(wù)功能是很難被一種模型所概括的——由用戶行為所觸發(fā)了線性交互邏輯。

這種類型的業(yè)務(wù)功能有一些比較明顯的特點:

    它并不復(fù)雜,通常是一連串操作的組合

    它由用戶行為所觸發(fā),也可能會涉及一些連續(xù)交互的功能。

    完全由業(yè)務(wù)邏輯所主導(dǎo),并沒有太多的共同點

通常情況下,這類邏輯就大量分散在系統(tǒng)的各個組件內(nèi)部,看上去像是某些事件的回調(diào)函數(shù)。但是隨著需求的不斷迭代,就會讓組件變得非常膨脹,以至于會影響整個組件的可維護(hù)性。

由于每個功能都是完全按照需求所定制化開發(fā)的,在一些常見業(yè)務(wù)功能都被高度封裝的情況下,多個功能之間的銜接,依然需要工程師人工編寫代碼來進(jìn)行完成。

這樣就會造成一個問題——功能的復(fù)用程度并不是特別高,因為有相當(dāng)一部分的代碼都是膠水代碼,是無法被復(fù)用的。所以想要提升整體的代碼復(fù)用性,就需要去思考,如何才能減少膠水代碼的開發(fā)。

分析交互邏輯的內(nèi)部細(xì)節(jié)

倘若仔細(xì)去分析之后就會發(fā)現(xiàn),組合通用邏輯的膠水代碼,不管是執(zhí)行同步的操作還是異步的操作,它都是以線性的方式去執(zhí)行,相比組件與組件之間的關(guān)系來說,交互邏輯這類代碼的結(jié)構(gòu)都比較簡單,它們都是在上一個操作完成之后才能去執(zhí)行下一個操作,當(dāng)中間遇到了一些執(zhí)行錯誤或者異常時,都是退出這個操作就結(jié)束了。

task1 --> task2 --> task3 --> task4

所以,這個問題就可以被轉(zhuǎn)變?yōu)槿绾握业揭环N能夠去結(jié)構(gòu)化同步或者異步操作的機制。

多個異步操作可以使用Promise進(jìn)行串行調(diào)用,同步的操作也可以被包裝成立刻返回的異步操作。所以可以使用Promise來將異步和同步之間的差異進(jìn)行打平。

串行調(diào)用在程序的世界中是非常常見的操作,例如reduce函數(shù),就是一個非常好的例子。如果能夠?qū)⒚恳粋€操作的調(diào)用,放置在一個數(shù)組中,那么就可以使用一次調(diào)用,來進(jìn)行批處理操作。

批處理的數(shù)據(jù)來源

對于每個交互邏輯來說,它都需要讀取一些參數(shù)來完成它的工作。比如發(fā)起請求需要參數(shù),彈出確認(rèn)框需要提示信息,數(shù)據(jù)驗證需要輸入數(shù)據(jù)。這些操作的數(shù)據(jù)來源有可能是來自于用戶觸發(fā)事件時的事件對象,也有可能是來自于當(dāng)前整個應(yīng)用中狀態(tài)的數(shù)據(jù),也有可能是來自于上一個操作的返回值。

所以如果要做這樣的一套批處理機制,讓每一個操作都能很順暢的運行的話,那么封裝所有來源的數(shù)據(jù)就是一件很有必要的事情了。

因此就需要在調(diào)用每一個操作所封裝的函數(shù)之前,把當(dāng)前所有的數(shù)據(jù)信息都收齊起來,組裝成一個對象傳入到函數(shù)中,來滿足不同的業(yè)務(wù)需求所需要的數(shù)據(jù)。

每一個操作在執(zhí)行的過程中,都有可能讀取以下來源的數(shù)據(jù):

    上一個操作的返回值

    事件觸發(fā)的時候,傳遞的值

    全局應(yīng)用的狀態(tài)

當(dāng)然,批處理還需要具備錯誤能力——當(dāng)任何一個操作返回的異常,整個操作就會直接被終止。

配置聚合和控制中心

任何零散的事物要想有組織的進(jìn)行工作,就必須要有控制中心。

在過去,處理交互邏輯是非常的分散的,即使現(xiàn)在有了類似于reduce的批處理操作,如果它依然是散步在一些不為人知的角落,這依然無法解決分散所導(dǎo)致的混亂問題。所以我們還需要將批處理的配置聚合在一起,并放置在最顯眼固定的位置,讓每一個人都知道想要找到這段邏輯是如何工作的,就需要看這里就夠了。

因此就需要思考,這樣的一個包含所有操作的信息的控制中心,應(yīng)該放置在哪里比較好。

頁面中的組件,都是以樹狀的結(jié)構(gòu)進(jìn)行組織的,那么不管這個頁面中組件的數(shù)量有多大,這些組件一定都會有一個最頂層的父級組件。所以這個站在金字塔最頂層的組件,就是放置控制中心的最佳選擇,怎么看起來感覺和現(xiàn)實世界中的情況差不多(笑。

而在React應(yīng)用中,直接和狀態(tài)通信的容器組件,就是聚合配置信息的組件了。也就是為什么在RCRE中,Task功能是作為Container組件的一個屬性的存在。

而流程式任務(wù)管理,正是RCRE的任務(wù)組功能背后的思想,通過這樣的一套機制,就能過去分散的交互邏輯,有跡可循,易于調(diào)整。

更便捷的表單驗證

表單一直都是中臺領(lǐng)域中開發(fā)成本高的代表。它含有數(shù)不清的交互場景,也是業(yè)務(wù)需求最頻繁改動的重災(zāi)區(qū)。

實現(xiàn)單個表單驗證并不是很難的一件事情。表單驗證的目的就是要去驗證用戶輸入的組件數(shù)據(jù),通過驗證數(shù)據(jù)的合法性來給予用戶一些反饋。因此表單驗證就只有2個功能,第一是組件數(shù)據(jù)的改變觸發(fā)驗證,第二是將驗證結(jié)果反饋給用戶。

頁面中的數(shù)據(jù)是多變的,實現(xiàn)一個全面的數(shù)據(jù)驗證功能,光在組件的onChange事件內(nèi)添加鉤子來觸發(fā)驗證是遠(yuǎn)遠(yuǎn)不夠的,因為組件的數(shù)據(jù)不光來自于自己,還有可能會來自于其他組件。除此之外,針對頁面輸入框這種特殊的組件,觸發(fā)表單驗證還有onBlur事件這樣特殊的交互。

因此實現(xiàn)數(shù)據(jù)的驗證功能就需要圍繞三個方面來開展,第一是onChange事件的觸發(fā),第二是組件所讀取的數(shù)據(jù)發(fā)生改變時觸發(fā),第三是onBlur事件這種特殊場景。

而對于頁面中的結(jié)果反饋,因為它涉及到組件的渲染,所有是需要通過一個統(tǒng)一的狀態(tài)來進(jìn)行控制,這樣才能通過組件渲染到頁面上,進(jìn)而給予用戶提示。

所以總結(jié)來看,實現(xiàn)一個組件的驗證功能不光光是一個簡單的數(shù)據(jù)校驗邏輯,而是要去完成以下的工作:

    對數(shù)據(jù)的校驗邏輯

    onChange事件鉤子

    onBlur事件的鉤子

    組件更新時對數(shù)據(jù)變更的判斷

    存儲表單驗證狀態(tài)的State

    展現(xiàn)錯誤信息的組件

以上就是完成一個組件驗證所需要的工作了,但是這并不是最煩人的地方,最讓開發(fā)者頭疼的,是以上這些工作,每個需要被驗證的組件都要完成,那么需要去寫的代碼可就多了去了。

利用狀態(tài)來驅(qū)動表單驗證

仔細(xì)觀察這些觸發(fā)表單的場景之后會發(fā)現(xiàn),上訴2,3,4點的是業(yè)務(wù)中最常見的應(yīng)用場景,同時這三點的背后,也和狀態(tài)的更新完全保持一致。因為無論是onChange事件還是onBlur事件,還是對數(shù)據(jù)變更的判斷,都是先有組件的狀態(tài)變更,再有的驗證,因此充分利用這個特性來節(jié)省工作量,就是解決2,3,4這三類問題的突破點。

表單驗證和組件狀態(tài)變更是同步變更的,那么只需要在組件變更的不同生命周期和回調(diào)函數(shù)內(nèi),添加觸發(fā)表單驗證邏輯的鉤子,就能很好的讓表單也跟著組件的狀態(tài)一起變化。

表單驗證的常見業(yè)務(wù)場景

通過上的分析和方法,表單驗證可以被狀態(tài)自動觸發(fā),所以我們可以把通過狀態(tài)來觸發(fā)表單驗證所有的場景都列舉出來:

    組件觸發(fā)onBlur事件來觸發(fā)驗證

    組件觸發(fā)onChange事件來觸發(fā)驗證

    通過一個接口來驗證數(shù)據(jù)

    組件被其他組件所聯(lián)動來觸發(fā)驗證

    特殊驗證場景,比如特定的驗證邏輯

    組件被刪除也要同步清空組件的驗證狀態(tài)

同時,除了和狀態(tài)之間的關(guān)系,表單還有一些它所特有的場景:

    通過點擊提交按鈕,在發(fā)送請求之前觸發(fā)所有組件的驗證

    跳過被禁用按鈕的驗證功能

    多組件之間的驗證相互互斥

同時表單的禁用特性,還會和組件聯(lián)動有關(guān):通過一個組件,來控制另外一個組件的禁用屬性,進(jìn)而操作驗證狀態(tài)。

提供表單特有場景下的支持

根據(jù)以上的分析,表單有三個特有的場景需要被支持。對于第一個場景,點用戶點擊了提交按鈕的時候,最外層的Form組件會觸發(fā)onSubmit事件,因此可以為開發(fā)者提供一個封裝好的回調(diào)函數(shù)給開發(fā)者使用。在這個回調(diào)函數(shù)內(nèi)部,需要去依次去觸發(fā)每個組件的驗證功能,來進(jìn)行全局的校驗,來確保提交的時候,每一項都驗證通過。

在表單中,被禁用的組件是不需要驗證功能的,因為用戶無法更改組件的輸入,那么驗證也就沒有了意義,因此還需要專門監(jiān)控組件的disabled屬性以便當(dāng)組件被設(shè)置為禁用的時候,立刻充值組件的驗證狀態(tài)。

對于組件驗證互斥這種特殊的驗證邏輯,我們可以將它看作是一種將組件狀態(tài)和驗證狀態(tài)進(jìn)行整合的功能。因為要想實現(xiàn)驗證互斥,就必須要去讀取其他組件的驗證狀態(tài),并將自身取反,因此就只需要給開發(fā)提供一個可以自定義擴展驗證的功能就足以,具體的專門邏輯實現(xiàn)交給開發(fā)者處理。不過這里需要注意的是,在提供自定義驗證的同時,還要給開發(fā)者提供讀取全局狀態(tài)的能力,因為實現(xiàn)這種功能不僅要讀取自身的數(shù)據(jù),而是要讀取來自其他組件的數(shù)據(jù),這是一個需要注意的地方。

在RCRE中,組件都已經(jīng)完全具備此類功能,能夠自動幫助開發(fā)者完成那些由各種狀態(tài)變更而觸發(fā)的表單驗證場景。

表單自身的私有狀態(tài)

由于表單也需要來存儲當(dāng)前的驗證信息和錯誤信息,因此表單也需要和組件的一樣,需要持有一些狀態(tài)。

因此想要節(jié)省開發(fā)表單時,驗證和錯誤信息的開發(fā)工作量,就需要為開發(fā)者提供一個通用的狀態(tài)存儲功能。同時表單的狀態(tài)并不是類似于組件的狀態(tài)那種,會有聯(lián)動的功能,每個組件的驗證都是相互獨立,只為當(dāng)前組件所負(fù)責(zé)。

因此就可以直接使用React State這種輕量級的狀態(tài)管理功能來完成組件驗證狀態(tài)的持有,通過將其封裝成一個React組件,就能方面開發(fā)者進(jìn)行使用,這也就是RCRE中RCREForm />組件背后的思想。

除了一個存儲整個表單狀態(tài)的組件,每個組件的驗證狀態(tài)還需要實時同步到這樣的統(tǒng)一存儲區(qū)域。因此就需要像上文所介紹的和組件通訊的機制類似,采用React Context API來實現(xiàn)組件驗證狀態(tài)和之間的通訊,以完成表單驗證狀態(tài)的同步,這就是RCRE中組件背后的思想。

有了這兩個機制,開發(fā)者就不需要手動去編寫實現(xiàn)來維護(hù)表單的驗證狀態(tài)了,所以對于上述第五點和第六點所帶來的重復(fù)性工作也就迎刃而解。

寫在最后

這篇文章所有的內(nèi)容,就是RCRE這個庫背后所有的設(shè)計思路和思想了,想必你看到這里也能夠理解為什么會有RCRE這樣的庫誕生了。如果有興趣想繼續(xù)了解這個項目,可以點擊下面這個鏈接:

github.com/andycall/RC…

如果有任何問題,歡迎在下方留言,我盡可能將內(nèi)容做到更好。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/7287.html

相關(guān)文章

  • AI中臺:一種敏捷的智能業(yè)務(wù)支持方案

    摘要:月日晚點,線上直播,中臺一種敏捷的智能業(yè)務(wù)支持方案金融科技領(lǐng)域,能解決什么問題在宜信年的發(fā)展歷程中,圍繞普惠金融和財富管理兩大業(yè)務(wù)板塊,宜信陸續(xù)推出了宜人貸宜人財富致誠信用博城保險等多個產(chǎn)品,技術(shù)已被廣泛應(yīng)用到各產(chǎn)品的業(yè)務(wù)線中。 [宜信技術(shù)沙龍】是由宜信技術(shù)學(xué)院主辦的系列技術(shù)分享活動,活動包括線上和線下兩種形式,每期技術(shù)沙龍都將邀請宜信及其他互聯(lián)網(wǎng)公司的技術(shù)專家分享來自一線的實踐經(jīng)驗,...

    Chaz 評論0 收藏0
  • 《阿里云前端技術(shù)周刊》第二期

    摘要:作者也樹校對染陌素材也樹英布阿里云前端技術(shù)周刊由阿里云智能商業(yè)中臺體驗技術(shù)團(tuán)隊整理編寫。如何在工作中快速成長致工程師的個簡單技巧工程師成長干貨,全文提綱如下,圖片來自阿里技術(shù)公眾號關(guān)于我們我們是阿里云智能中臺體驗技術(shù)團(tuán)隊。 作者:@也樹 校對:@染陌 素材:@也樹、@英布 《阿里云前端技術(shù)周刊》由阿里云智能商業(yè)中臺體驗技術(shù)團(tuán)隊整理編寫。 知乎:阿里云中臺前端/全棧團(tuán)隊專欄 Github...

    kyanag 評論0 收藏0
  • 前端能力中臺化之路———— Fusion Design 成長史

    摘要:快速摧毀敵軍設(shè)施,殺傷有敵軍生力量,最小化己方傷亡。所以前端工程師在修改完樣式以后,需要反復(fù)和設(shè)計師還原度的問題。前端工程師依照主題包和設(shè)計稿進(jìn)行前端工程開發(fā)。前端工程師很開心,因為不用去投入開發(fā)組件庫和調(diào)整還原度。作者: 暮塵 2019年05月11日在上海舉辦 FDCON 2019。筆者有幸受到邀請,參與這次盛會。這篇文章就是演講內(nèi)容的文字提煉版。 淺談中臺 在開始正文內(nèi)容之前,先簡單聊聊...

    MycLambert 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<