螞蟻前端研發(fā)最佳實踐
(給前端大學(xué)加星標,提升前端技能.)
作者:云謙
https://github.com/sorrycc/blog/issues/90
本文是阿里高級前端技術(shù)專家云謙在 2019.11.15 成都全棧大會分享的文字稿,介紹了螞蟻前端研發(fā)的最佳實踐,其中提取了三個比較重要的點,每個點都是螞蟻實踐和深入思考后的結(jié)果,希望能對大家有所啟發(fā),歡迎探討。
開篇

準備這個題目時我 google 了下前端最佳實踐,排在前面的是講前端代碼規(guī)范,語意、可讀性、編碼規(guī)范、空格還是 Tab 等等,我覺得這是我們第一代的最佳實踐。
而現(xiàn)在都 9012 年了,最佳實踐也經(jīng)歷了很多代的變更,下面是我們在這方面的思考和實踐。
自我介紹

目錄

為什么要有最佳實踐?


不知大家在這些方面是否有疑惑?
前端發(fā)展快,每次發(fā)布 Umi 版本時,除了點贊,還有人求別發(fā),表示實在學(xué)不動了。右邊這張圖是之前的朋友圈看到的,轉(zhuǎn)到公司群里,有共鳴的人不少,說明一定程度反應(yīng)了現(xiàn)在前端社區(qū)的情況。
面對海量“輪子”,我應(yīng)該學(xué)哪些,不學(xué)哪個?,我的前端知識點學(xué)習(xí)列表已經(jīng)是完全學(xué)不完的狀態(tài),比如社區(qū)上光數(shù)據(jù)流方案就有幾百個,其中值得一看的也有四五十個吧。
然后,大家在創(chuàng)建項目時,是否也有過選擇困難?

這里舉一個具體的例子,
框架
基于 React 的框架
語言
CSS 方案
數(shù)據(jù)流
請求庫
等等
可以發(fā)現(xiàn),每個點都會有不少選擇,并且有時還真的很難選,因為每個人看待它的角度不同。所以,對于開發(fā)者來說,真的有點太難了!他只是想完成需求,然后回家睡覺,為啥需要選這么多,就不能給個默認的嗎?

然后,
如果每個項目都要選,我覺得會很難。當然,也有人可能會覺得是一種樂趣。
而對于團隊而言,如果每個項目的選擇還都不一樣,那么團隊的研發(fā)成本和效率都會是問題。想象一下,如果一個同學(xué)換個項目組,需要接觸完全不同的技術(shù)棧。
所以,對于團隊而言,保持一致非常重要。

那么,如何保持一致?不同團隊會有不同的選擇,通常有這幾類,
文檔
腳手架
框架
約束能力和迭代能力也是逐步遞增。
我們最早應(yīng)該是用的文檔。比如做一些代碼約定,用 Tab 還是空格,用兩個空格還是 4 個空格,行尾要不要加分號等等,這一類主要靠開發(fā)者自覺,所以我覺得不太靠譜,這也是為啥后來有 eslint。
腳手架比文檔好點,但也依賴開發(fā)者的自覺性,因為還是可以隨便改。前幾年 React 社區(qū)上有不少做的很好的腳手架,但現(xiàn)在基本上都沒有活躍的了。
第三種方式是框架,他的約束性可以做的比較強。比如約定用 less,如果開發(fā)者用了 sass,就給他報個錯。同時相比其他兩種方式,還有迭代能力。腳手架交給用戶之后是很難更新的,框架則是自己更新后,開發(fā)者的項目自動生效。
當然,這三者不是互斥關(guān)系,可以都用嘛。

然后如何決定用啥方案,用 SASS 還是 LESS,要不要用 TypeScript,甚至目錄用復(fù)數(shù)還是單數(shù)這種極其無聊的事情。
不同團隊會有不同的選擇,
STAR 數(shù),大家通常會選 STAR 數(shù)多的,社區(qū)認同感很重要,比如 DVA 在螞蟻的推廣就是先從社區(qū)做起的
簡單 vs. 規(guī)范,有人會選擇概念少而簡單的,有人會選擇概念多但看起來更規(guī)范的
先入為主,先占坑的往往具有優(yōu)勢
老板喜歡,?
老板喜歡其實 “很重要”。有些大家吵很久但決定不了的事,往往會很自覺地找老板或者德高望重的同學(xué)進行拍板,我們也是如此。
螞蟻前端的選擇


我們在不同時期的最佳實踐是不同的,曾經(jīng)還開發(fā)過 spm,不自量力地試圖挑戰(zhàn) npm + webpack 組合,雖然失敗了,但敢想也是一種勇氣。(做 spm 時,webpack 還沒出來)

我們有很多方向,然后每個方向又有很多選擇,圖上是我們目前的選擇。
從這里可以看到幾點,
選擇的內(nèi)容基本上是社區(qū)主流的,不脫離社區(qū)是基本原則
很多子方向都選擇了自研或者正在考慮自研
為啥要自研呢?

我覺得自研會帶來一些好處,
自主權(quán) vs. 成本,在擁有自主權(quán)的時候,需要評估其帶來的成本,以及潛在的棄坑可能
定制化,
需求滿足,社區(qū)方案有時并不能很好地契合我們的需求,尤其當我們很深入地去使用的時候
售后服務(wù),出錯是能找到 owner 的同學(xué)是非常重要的一點
有些開源庫看起來美好,但真正用下來會發(fā)現(xiàn)坑不少。比如組件的文檔工具,目前是選擇的 docz 和 storybook,但兩者用地都有些說不出來的不舒服,并且和 umi 是兩個生態(tài)的東西,所以我們正考慮基于 umi 開發(fā)自己的文檔工具,可能叫 umipress 或者 father-doc 。

沉淀的方式是以框架為主,文檔、腳手架、資產(chǎn)市場為輔。
框架具有更強大的約束性和迭代能力,這也是我們所需要的
對外是 Umi,面向社區(qū)
對內(nèi)是 Bigfish,在 Umi 的基礎(chǔ)上解決流程和業(yè)務(wù)問題
插件和插件集


我們把使用到的技術(shù)都沉淀到框架(Bigfish)里。框架像是一個魔法球,把各種技術(shù)棧吸到一起,加工后吐給用戶,以此來支撐業(yè)務(wù)。
對于用戶來說,Bigfish 框架是唯一依賴。唯一依賴會帶來一些實際的好處,這也是我們一直在內(nèi)部堅持這一點的原因,
技術(shù)收斂,保持團隊開發(fā)模式的一致性
無痛升級,我們既要保持對社區(qū)的技術(shù)跟進,又要讓業(yè)務(wù)項目跟上步伐,這些中間的屎只能讓框架吃掉,讓開發(fā)者盡可能地無痛升級
應(yīng)用治理,相比散落的遍地開花的依賴,唯一依賴可以讓我們更好地推動用戶升級框架,因為只要管一個點即可
唯一依賴的問題就是大而全,雖然看起來挺不優(yōu)雅,但實際用過之后會發(fā)現(xiàn)還蠻香的,除了一開始安裝他會有點慢。(這一點我們后續(xù)會通過啟動器解決)

做了技術(shù)棧收斂之后,我覺得對外可能夠了,但對內(nèi)還遠遠不夠。
接流程,讓開發(fā)者能更順暢地跑通創(chuàng)建、本地開發(fā)、聯(lián)調(diào)、部署、發(fā)布和統(tǒng)計
接后端框架,后端可能是 Java、Node 或者 PHP(?),不同后端對于前端產(chǎn)物的要求會不同,在框架里做好對接,開發(fā)者就不用費心思了
接場景,場景有很多種,在框架層也需做好對接。舉一些例子
SPA 應(yīng)該是目前用地最多的一種應(yīng)用類型,但有時也會不滿足需求
比如運營頁面,多個頁面之間沒有一點點關(guān)系,也不需要互相跳轉(zhuǎn),用 SPA 就沒有意義,這時候 MPA 可能更適合
比如語雀,我們的文檔平臺,他有前臺、有后臺、有 PC 端、有無線端,如果整體是一個 SPA,不僅尺寸大,公共依賴的提取是個問題,不同場景之間可能還會相互影響,這時候,多 SPA 的組合會更適合他
微前端前面已經(jīng)提過
SSR 和 Prerender 則是為了更好的瀏覽器性能,順便解決 SEO 的問題
接服務(wù),比如登錄服務(wù),統(tǒng)計服務(wù),問卷服務(wù),評論服務(wù)等等
實現(xiàn)方式是一“件”接入,這里的件是插件,一個插件實現(xiàn)一個功能。然后,我們就有了很多插件。

有了插件之后,我們可以篩選一些插件出來形成插件集,以滿足某個業(yè)務(wù)的需求,類似 babel 的 plugin 和 preset,或者 eslint 的 rule 和 config。
**這種方式首先可以滿足不同業(yè)務(wù)的需求。**比如無線業(yè)務(wù),會比較關(guān)注性能,所以可能會選一個切 preact 到 react 的插件、極速版補丁插件、高清方案、fastclick 等等,形成一個插件集。
**然后還可以滿足一個技術(shù)的不同實現(xiàn),**在一個業(yè)務(wù)類型豐富的大團隊中,是允許有不同的選擇的。比如數(shù)據(jù)流,大家的選擇可能不同,有些用 dva,有些用 hooks,有些用 mobx,有些自研一套;比如補丁方案,有常規(guī)版、極限版,還有終極版。

這是 umi 的插件三態(tài),講過好多次了,文字稿里就不重復(fù)了。

這是 umi 插件的示例。想提一點的是,會用 umi 和會寫 umi 插件是兩個完全不同的狀態(tài),會寫 umi 插件,你基本可以魔改 umi 內(nèi)部 70% 的功能,可以此來達到滿足需求業(yè)務(wù)需求的目的。


資產(chǎn)市場和場景市場


先來看下開發(fā)者的時間都去哪了。這是我咨詢了一些同事拍腦袋整理的,不太準確。
20% 流程相關(guān),從創(chuàng)建到發(fā)布和發(fā)布后統(tǒng)計
40% 組件使用和開發(fā),如果有合適組件,直接使用;如果沒有,花時間開發(fā)
30% 交互場景,解決遇到某個交互場景如何處理,以及處理來自后臺的請求,把數(shù)據(jù)和視圖串起來
10% 其他
知道了時間分配后,大家應(yīng)該知道投時間去解決哪部分的問題,才能真正達到提效的目的了吧。

資產(chǎn)市場用于解決 40% 的開發(fā)者時間,非常重要。分為四個概念,
基礎(chǔ)組件,antd
業(yè)務(wù)組件,基于 antd 封裝的具有業(yè)務(wù)屬性的組件,不對外
區(qū)塊,組件的使用片段,區(qū)塊是為了方便地把代碼片段加到項目代碼中
模板,多個區(qū)塊組成的頁面

而資產(chǎn)市場要真正達到提效的目的,我覺得還需要解決一些關(guān)鍵的點,才能讓整個流程跑起來。
資產(chǎn)質(zhì)量,組件參考 antd,區(qū)塊和模板是實實在在要被添加到用戶項目的代碼,我覺得比組件更難,需要形成對什么是好代碼的共同認識,誰都不希望自己的項目變臟
打通上下游,包括組件的生產(chǎn)和消費。生產(chǎn)方是設(shè)計師和前端,需要保證組件的本地開發(fā)、文檔、打包、發(fā)布等環(huán)節(jié);消費方也是設(shè)計師和前端,資產(chǎn)市場不僅是給前端用的,設(shè)計師也得用,只有前端拿到的設(shè)計稿有大量可以對應(yīng)的資產(chǎn)時,前端開發(fā)才能真正提效,所以,設(shè)計師是否有能力讓資產(chǎn)市場覆蓋 50% 甚至 80% 的場景非常重要

這是內(nèi)部的資產(chǎn)市場和外部開源的 antd。


這是資產(chǎn)市場通過 umi ui 的方式使用,支持區(qū)塊、模板以及布局區(qū)塊。

右圖來自開源庫 Friend-List,這是一個 suggestion 的實現(xiàn),他可以簡單做,也可以復(fù)雜做。復(fù)雜做的話,細節(jié)點就會很多,比如:
每次輸入都要做請求
快速輸入的時候,要使用最后的請求,并且取消前面的請求
輸入需要同步到 url
輸入還需要同步到 history,支持前進后退
請求加緩存
請求出錯處理
...
而如果每個開發(fā)者都要去關(guān)心這些細節(jié),會很難,成本也很高。那么如何讓開發(fā)者做到又快,產(chǎn)品體驗又好,我覺得可能需要場景市場,用于解決 30% 的交互場景需求。
沉淀方式可以用 hooks + 文檔的方式;覆蓋面從最簡單的 CURD 開始,到各種復(fù)雜場景。

這里是部分的場景舉例。
理想的工作流圖。
強約束的垂直領(lǐng)域框架

基于前面講的插件和插件集的方式,我們已經(jīng)能夠滿足各種豐富的業(yè)務(wù)場景,但是仍然給予了用戶很多選擇,選擇包括選擇插件,以及 umi 自身的大量配置項。
對于一些垂直領(lǐng)域,其實還可以做到更好,所以我們最近一直在思考“螞蟻前端應(yīng)該如何寫中臺代碼”。

有幾個關(guān)鍵的思路,
專治,不提供自由的技術(shù)棧選擇一定程度上會限制開發(fā)者的,但是效率高的,就看你要哪個了
極簡,不僅僅是簡單,還要優(yōu)雅;不僅要寫地少,還要寫地好
然后就強約束、配置化和約定化展開聊下。

前面我們已經(jīng)了解了一致性的重要性,所以何不把這一點做到底呢?
只能用 TypeScript,用 JavaScript 會報錯
只能用 less + css modules,用 sass、stylus、css in js 會報錯
只能用內(nèi)置的數(shù)據(jù)流方案,用 redux、mobx 等等會報錯
等等
圖上只列了一部分。
這里的有些約束甚至?xí)行┓慈祟悾矣X得約束越強,越能保持大家的一致性,如果我們已經(jīng)把這條路探地很清楚了,少給選擇或許是更好的選擇。有些限制還不確定是不是好的方式,但是第一版會盡量把規(guī)則收攏地緊一些。

配置化不僅是框架和插件的配置,還包括 UI 。
右圖是 ant-design-pro 的圖,其中 LOGO、導(dǎo)航、菜單對于 90% 的每個頁面來說都是固定的,變化的只有右下的頁面區(qū),所以我們何不把固定的部分做成配置呢?
比如:
export?default?{
??layout:?{
????logo:?string;
????title:?string;
????renderRender:?function;
????logout:?function;
??},
??routes:?[
????{
??????path,
??????//?菜單配置
??????menu:?{?name,?icon,?showBreadcrumb?},
??????//?權(quán)限配置
??????access,
????},
??],
}Layout 是其中一個例子,還可以有更多 UI 的配置化。這也是在一定程度在像 low code 的模式靠,我覺得某些研究地很透的垂直場景下,low code 能讓研發(fā)更高效。
所以我們把適合做成配置的全部配置化,而不能配置的,則會走約定化。

之前有用過 ruby on rails 框架,特別喜歡那種約定化的編碼方式,所以我們希望把他也搬到前端研發(fā)流程里。
建一個 locales 目錄,就擁有了國際化
建一個 models 目錄,就擁有了數(shù)據(jù)流
建一個 mock 目錄,就擁有了數(shù)據(jù) mock
建一個 access.ts 文件,就擁有了權(quán)限策略
...
看起來很黑盒,按照我們約定的方式編碼,并且只能這樣編碼,然后他就能 run 起來。

這是之前在朋友圈看到的圖,大家體會下,但這就是我們想要實現(xiàn)的樣子。

極簡數(shù)據(jù)流是整體方案的其中一環(huán)。
右邊是之前做數(shù)據(jù)流調(diào)研時做的整理,發(fā)現(xiàn)那么多數(shù)據(jù)流方案基本都是在這些方案上的差異,而要選哪個就看你對哪些方面比較關(guān)心。這部分展開聊比較長,之后會額外寫一篇文章介紹。
然后我們還調(diào)研了下公司內(nèi)部的中臺項目,發(fā)現(xiàn)大部分是簡單的 CURD,并且全局數(shù)據(jù)使用較少,比如通知、登錄、當前用戶信息等。所以,我們可能是需要一個不那么復(fù)雜的,用起來又很簡單的數(shù)據(jù)流方案。

最終討論下來的方案有幾個特點,
基于 hooks,在看到?swr?之后,我開始有點覺得在數(shù)據(jù)流里用 hooks 可能是未來的趨勢,因為大量的交互場景都可以通過 hook 沉淀,但也有一點點擔心
和框架強綁,脫離框架我們可能沒有優(yōu)勢,但是有框架加持,就能做到比社區(qū)大量的 hooks 數(shù)據(jù)流都“好用”,因為中間復(fù)雜的事情可以交給框架處理,比如手寫 Provider,比如自動處理 model 依賴等等
約定式 model 定義,在 models 目錄下建文件導(dǎo)出 hooks 就是一個 model
單一 API,在組件層或者 model 層通過?
useModel?來使用
總結(jié)

文字就不復(fù)述了。
這里和大家分享了螞蟻前端研發(fā)實踐中三個重要的點,但其實還有更多的點,比如說 UMI UI,如果感興趣,可以來聽我在 12 月 GMTC 深圳的演講。
分享前端好文,點個?在看?
