作者 | 被單

資料流是前端一直以來都存在的一個問題,我們項目沉澱了一套最佳實踐,如有問題,歡迎探讨😄
在舊的 Done 項目中,代碼複雜度高,已經到了“牽一發而動全身”,技術債極高的情況。由于舊代碼“錯綜複雜”,導緻實作一個簡單的功能,都需要比正常時間多2~3倍的工作估時。就像下面這張圖的情況一樣。
我們仔細分析下現有的業務,會得出下面的業務特性:
- 強領域 (比如:項目/檔案/團隊/使用者領域,在很多元件都會同時調用某個領域下的方法,靜音/點贊/轉移項目……)
- 單頁面多且複雜,元件過多,多層嵌套元件間通信多。
- 業務已經實作了從 0~1 , 目前正處于從 1~n 的階段,在這個階段,會大量基于使用者建議而做出産品互動的調整以打磨精品。需求數量增長,前端變動大,前端人力瓶頸大。
基于上面的業務特性,我們再分析目前項目中的問題:
- 模闆代碼過多,影響開發效率和可維護性
- 資料流螺旋呈網狀調用(強耦合),代碼複雜度急劇上升,牽一發而動全身
- 資料全局化
- 原始資料與展示資料轉換
- UI 與 資料邏輯耦合,複用低
根據上面的特點和問題,我們有以下的訴求:
- 簡單高效
- 拒絕模版代碼
- 降低代碼複雜度(降低關聯影響)
- 提升複用性
- 極大程度的複用 UI / 邏輯層
- 複用 資料轉換
基于上面的問題和分析,下面将一步步推導新的架構圖 & 技術選型。
問題分析
一、 UI 與 資料邏輯耦合複用度低 & 原始資料與展示資料轉換
原先的整體架構如下:Store 與 視圖層混合在一起, 一起處理使用者行為和業務邏輯,耦合度高,複用率低。
架構演進:
改進點:獨立的 Store 層, 封裝應用程式與業務邏輯相關的資料以及對資料的處理方法, 在此處獨立寫邏輯,支援多處元件複用一個邏輯,與視圖層獨立, 一個邏輯可以複用于多個元件。
衆所周知,分層是為了解耦。假設 Store 層發生了改變,那麼在視圖層不需要變動的,隻需要修改 Store 層即可,這樣改動的地方就少了,也提升了開發效率&可維護性。
但是,我們還遇到一些場景,當後端接口發生字段變動,要改動的地方實在太多太多。同一個接口,在不同地方展示,由于多人維護,他會經過多次資料轉換,最終映射到前端界面。是以,我們再多一個API防腐層,專門處理前端界面和背景界面的資料轉換,這樣,一旦資料結構發生變化,我們也隻需要在 API 層修改即可,無須關注到多個界面元件中。
改進點:獨立 API 層, 銜接後端服務與前端服務, 若背景接口發生變化,可在此處進行統一修改,無需多處代碼進行修改,友善在該層進行資料檢驗, 預防背景傳回錯誤的字段等等。
通過上面這樣的分層,上面2個問題就迎刃而解了。
二、資料流網狀調用
我們對 Store 和 視圖進行了分層,但是随着項目的疊代,又出現了下面這種情況,資料流呈網狀調用。
舉一個例子:對項目設定靜音
在舊的代碼中,工作站會調用到團隊中的資料,也會修改到團隊中的資料,甚至接口回調後,他還會對各個地方涉及到靜音界面的相關資料都進行修改。
這會帶來一個問題,由于是全局共用的 Redux ,我們在 A 地方調用這個方法,這裡會更新 B 界面(B 地方并沒有出現在使用者的界面上),也就是說,修改 A 地方的 action ,還需要同時關注 B,C,D…… 這些地方(實際上,我們并不需要關注其他的地方)
我們知道分層可以解耦,對降低複雜度非常有效。那我們是否也可以對 Store 也進行分層,且限制他們之間互相調用?
重構後,同層級的 store 不能互相調用,若需要調用,則說明這個 store 要提升到上一層級(下文有詳細說明),換成這種方式之後,整個 Store 層也清晰明了,并且不會随着業務疊代而變得越來越複雜。
重構後,我們隻需要用下面短短幾行代碼就可以實作:
每個視圖元件(A/B/C/D)拿到調用“靜音”的回調結果,自己做更新界面的操作。
這樣子,由原來的一處代碼需要關注 N 處, 到現在隻需要一處代碼關注一處,大大降低了代碼的複雜度。
三、資料全局化
舉一個例子:在非父子元件之間共享傳遞一些狀态,我們會使用狀态提升來解決這個問題。但是如果此時元件之間的嵌套過深,那麼中間經過的元件都會幫忙傳遞這些無用的 props, 且如果需要傳遞參數或者增加 props ,都需要修改 A、B、中間元件 * n 個地方。
那麼這時候,我們就會将這些狀态放到全局 redux 中。但這樣,又會引來其他的問題,對于一些臨時保持的狀态,比如在中背景常見的場景:A 元件控制同層彈窗元件 E 的顯示隐藏狀态,而這些狀态對于使用者來說,并不需要儲存,是一次性的。
此時由于中間跨越的元件過多,我們将他放到了 redux 裡面去,久而久之,redux 中的 action 越來越多。慢慢的,我們每次修改都需要确認他是否也一樣影響了其他元件(實際上,這個 action 隻會在這個子產品中使用到,其他子產品都無須關注)。
對于 Redux 派的資料流管理,都是中心化的。看了大部分的中背景産品,需要全局共享的資料并不多,一般隻有使用者 user 資訊。在這個背景下,我們看向 mobx ,他天然支援多個 store 。那怎麼去設計 store ?
高内聚對于提升項目的可維護性自然是一個好事,但是如果不控制好粒度,也容易引起問題。比如, 我們嚴格按照 React 官方的指導意見:如果多個 Component 之間要發生互動, 那麼狀态(即: 資料) 就維護在這些 Component 的最小公約父節點上
那麼按照這種劃分,我們的程式會出現成十上百個公約節點, 随着項目的疊代前進,曾經隻是 A, B 之間共享,後面變成了 A,B,C 共享, 最大公約節點又需要向上提升,在多人協作的情況下,太多的提升和變更很容易引起項目的不穩定,是以,我們劃分了下面三層 store ,最小粒度的 store 為子產品 store 。
映射到具體項目中的 Store 圖如下:
四、領域模型 DDD
我們如何去設計 Store ?我們使用了領域驅動設計,也就是 DDD 。
為何需要 DDD 呢?
- 在傳統的前端開發流程中,前端和業務是通過視覺稿來聯系的,一旦視覺稿發生變更,就意味大量的修改成本。目前産品到了 1~n 階段,視覺稿需求稿的變化是必然的。
- 一個複雜的産品,是由多人協作的,如果大家都是按照視覺稿去設計 Store ,那麼重複的邏輯會越寫越多,後面技術債也越來越大。
- 貼合業務
如果我們采用 DDD ,比如我們抽象一個領域模型「檔案」,在這裡面,存放檔案的相關操作:「修改檔案名,删除檔案,移動檔案…… 」,有了這樣一個穩定的領域模型,視圖層隻需要實作視覺稿群組裝業務邏輯,具備很強的靈活性,就好像搭積木一樣,底層的領域模型不需要變動,隻需要改動互動變更或視圖。極大提升了開發效率和維護性。
舉個例子,随着項目的疊代,關于檔案的相關操作:删除、移動等這些已經沉澱在領域模型中了,如果此時産品變更,删除的入口發生了變化,或者是增加了一個新的删除入口,那我們隻需要修改完視圖元件,然後在需要調用的地方調用下對應領域的 action 即可
按照領域的劃分, Store 之間的界限也更加清晰。
注意:DDD 不是一個架構,而是一種架構思想,是以前端在開發之前,要先細化需求,設計好 Store 再進行開發。
項目中已沉澱的領域
改進點:根據 Mobx 官方指導建議,除了頁面一些松散的狀态,還會根據整個業務特性沉澱一些通用的領域模型,這些可以根據不同頁面需要,注入到對應的 Page Store 中去。
領域驅動設計這個理念已經在我們的業務中摸打滾爬了幾個月,參與開發的同學都說好,在前端人力瓶頸大的情況下,背景同學也參與進來寫 Store ,Store 與 視圖同步并行開發,從串行開發到并行,極大提升了整體的開發效率。
技術選型
寫了這麼多,新的系統架構需要以下幾個特性,才能讓系統走得更快更遠:
- 合理的分層, UI / 邏輯分離
- 複雜項目 Store 的粒度細化很重要, 領域模型 DDD
- 拒絕模版代碼,提升開發效率
- 面向未來&相容舊代碼 -- 支援 hook & class
- 更好的 ts 支援
我們期望擁有這些特性,然後一個一個對比。
unstated-next
- 不知道要寫多少 provider ,寫法過于靈活,組織成領域模型需要大量改造(在一個小項目試驗過,最後随着業務越來越複雜, 放棄了。)
- 舊的業務邏輯需要改造,一旦遇到class的情況,就又要回到舊的寫法,在快速疊代業務的時候,是不可能停下來一個一個全部改造成 hook 的。
redux
- 大量的模版代碼
- 過于靈活的 Redux 調用(網狀調用)
- TS 支援改造成本大
為什麼選擇 mobx ?
✅少量/無 模版代碼
✅面向未來&相容舊代碼 -- 支援 hook & class
✅很友善地對業務進行分層,很友善地設計領域模型
✅TS 支援 0 成本
✅天然支援多态 Store ,去中心化更友善
✅需要顯示 DI, 解決了網狀調用的問題
關于 Mobx 的缺點業界也說了很多,無非就是以下幾個點:
- 狀态可以随意被修改 -- 解決方案:開啟嚴格模式限制,且隻可在 Store 中修改,不可在視圖元件修改
- 不支援 hook ,mobx-react-lite 已支援
綜合上面種種原因,如果為了這些特性,重新去造一個輪子或者改造一個輪子,成本遠遠比直接借助 mobx 的力量更大。是以,在調研了多種資料流方案之後,選擇了 Mobx 來支撐我們上面的架構。
總結
沒有最好的技術方案,隻有最适合業務的技術方案。我們從一個一個“業務痛點”推導出一套解決方案,并且已在實際項目中跑了幾個月,也獲得了不錯的效果。我們緻力于用技術的能力提升設計效率,賦能業務團隊以設計能力(LowCode,即設計線上)如有疑問,歡迎探讨(微信: A18814127)
關注「Alibaba F2E」
把握阿裡巴巴前端新動态