從廣告投放管理系統,到房屋設計審批系統,ERP 系統,稅務報表管理系統,衆籌信息管理平臺,研發流程管理系統,作前端這些年我開發了不少這樣的系統,如今你們好像喜歡叫他們中後臺系統,或者 B 端系統,我以爲他們是比較典型的複雜前端系統,這篇文章但願經過個人經驗總結一下開發這些系統時的難點和解決難點時須要關注的重點。前端
本文主要關注功能的場景和模型,而不是技術選型,技術方案對解決問題有幫助,可是不會改變問題的實質,好比用 Redux 相比用 jQuery,須要發的請求一個也不會少,只是在數據向視圖的轉化和事件的綁定等具體操做上有了簡化。瀏覽器
從交互上直觀地看,咱們能夠看到有如下典型元素的系統常常是更復雜的。表單,彈窗,標籤頁,分頁,加載更多,表格等。那麼這些元素爲何會使系統變得複雜呢?咱們逐一看一下:框架
從傳統網站開始,表單就是系統的難點,它的複雜在於用戶要經過它輸入數據,這還涉及到咱們你們都熟悉的表單驗證。在用了 Redux 等工具以後,問題獲得了必定程度的簡化,問題的模型也變得更清晰,表單的每一個項目都是一個狀態值,與用戶的輸入綁定,而各個表單項目之間還可能有關聯邏輯,甚至是關聯的數據請求,好比用戶選擇了這個項目的值以後,須要請求相關子項目的數據。還有咱們常見的 input suggestion,根據用戶輸入實時獲取數據給出可選項。表單校驗又會給每一個表單項增長錯誤相關狀態,包括是否有錯誤和錯誤信息等。在表單基礎上更復雜的交互有表單配合彈窗,表單嵌套表單等,主要模型是針對某個表單項須要選擇或者新增相關信息,好比表單中有地址項,而地址項能夠新增,這時候就涉及到模塊之間的通訊,在信息新增完成後須要根據新增信息更新表單數據。ide
很是常見的元素,主要是監聽點擊事件作模塊之間的切換,複雜在於每一個模塊均可能有本身的初始化等邏輯,而模塊之間又可能有關係,或者在某些狀況下須要在切換時重置模塊狀態。函數
也是比較常見的元素,作的是模塊內部數據的更新和增長,接收用戶的點擊事件,請求相應數據。工具
展現型的表格複雜度不高,通常只是有初始化的請求,數據加載後就直接渲染了。複雜的表格有兩個方向:fetch
經過上面對這些典型元素的分析,我將這些元素增大系統複雜度的緣由歸納爲兩個方面,一是增長了模塊的數目,二是增大了模塊內部的複雜度。而模塊內部的複雜度又涉及到用戶事件,數據請求和數據狀態三個方面,這三個方面又是互相關聯的,好比用戶事件常常會改變系統數據狀態,也常常觸發數據請求,數據請求返回的數據通常也會做爲數據狀態的一部分。網站
這個是一個功能相對獨立的功能塊。在技術上一般表現爲 Redux 一個子 Store 對應的部分,它通常有本身的一些狀態(state),可能須要發一些請求去獲取一些初始數據。通常模塊的生命週期是發請求作數據的初始化,響應用戶事件改變內部狀態,也可能會再發一些數據請求,有時可能須要對自身狀態進行重置或者從新發出初始化時的數據請求對自身數據進行刷新,在生命週期結束時還可能須要清理本身的狀態數據。設計
模塊增長以後的系統複雜度增長不是線性的,可能因爲模塊之間的互動造成幾何式增加,而模塊之間的通訊的方法也是前端關注的一個重點,Flux 模型解決的一個很重要的問題就是模塊間通訊方式混亂,在 Flux 數據單向流動模型下模塊之間的常見通訊方式精簡爲如下兩種。cdn
模塊之間通訊的時機通常在模塊初始和結束時,這樣的模塊關係好比兄弟步驟關係,上一步在結束時會把本身的信息存儲好並開啓下一步,或者父子關係好比上面咱們提到過的模塊中填寫地址時調用新建地址模塊新增地址,新增地址模塊結束後把新增地址信息傳遞回來。
若是模塊之間有頻繁的通訊,那麼他們極可能能夠概括在同一個大的模塊下,好比上面提到的表格和它外部的操做和篩選按鈕。
模塊多了就避免不了出現重複性的邏輯,好比權限校驗,多個模塊可能須要一樣的對操做者權限進行校驗並在無權限時給出提示的邏輯,還有例子好比多個模塊都須要根據鼠標的移動位置讓組件執行必定的邏輯。目前 HOC 和 React Hooks 是解決複用問題的主要方案,複用雖然不能讓模塊自己的複雜度下降,可是能夠減輕咱們開發多個一樣功能時的工做量。
這包括輸入框,按鈕等觸發的咱們常見的事件好比 onChange, onClick 和 onScroll 等等,他們毫無疑問是整個前端系統的核心,由於它們表明用戶的操做,而用戶的操做是系統運轉的入口,增長了入口天然增長了複雜度。
十年前 Gmail 開創先河向咱們展現了 Ajax 可以在瀏覽器上作出什麼樣的應用,開啓了前端大發展的十年。那爲何它是特別的?一直到如今咱們的 WEB 應用的核心功能是什麼?其實仍是隨時隨地的數據通訊能力和與之配合的局部刷新,這也是爲何 WEB 應用有一個名稱叫作 RIA —— Rich Internet Application,而數據請求的層次多少和密度大小也是模塊複雜度的重要因素。固然如今咱們的數據請求不止 Ajax 一種,還有 WebSocket 和 SSE 等,他們會讓數據請求的邏輯變得更加複雜。
有些狀況咱們的模塊不能只經過一次請求得到所有所需數據,好比接口瑣碎或者數據源複雜,就須要拼數據,好比請求了列表信息還得去請求每一個 item 的詳細關聯信息,因此有 GraphQL 解決接口 under-fetching 和 over-fetching 的問題,服務端提供數據,要解決請求的問題,改造服務端是一個重要的途徑。固然若是服務端不容易改造,不少時候前端仍是得本身解決問題。
Redux Saga 和 Redux Observable 就是爲了解決復瑣事件和數據請求邏輯而產生的,對 Redux 而言也就是處理 Action 和反作用(Side effects)。
Redux Saga 能夠用它的 Effects 簡單地描述競爭性請求(Race),不阻塞請求(Fork),等待所有請求完成(All)等;能夠控制包括數據請求在內的動做順序,好比典型的 Login Logout 流程,只有登陸以後才能夠作登出操做;還能夠在 Action 層內部經過 Put 和 Take 作訂閱模式,好比在第一個 Saga 中定義發起數據請求先後一系列動做的執行流程,而在第二個 Saga 中定義數據請求自己的流程,好比到多個地方獲取數據作組裝,第一個 Saga 會經過 Put 發佈數據請求動做,第二個 Saga 會經過 TakeEvery 訂閱這個動做作出反應。
Redux Observable 和它背後的 RxJS 的事件控制能力則更強大,好比能夠輕鬆地監聽 Triple Click 事件,能夠定義多個事件的組合觸發關係,好比定義多個事件必須都發生過才能夠觸發總體事件,然後續任何一個再發生都會觸發總體事件。
事件和數據請求,或者說是 Action 和 Side effects,他們的複雜度不只取決於數量,更取決於他們之間的時序和邏輯關係。
現代框架中的 state 概念,是單向數據流動的核心,React 和 Redux 等工具中都有,相信你們都能理解,系統須要的 state 越多也就表明系統更加複雜。
說了這麼多,你們可能會想,這些東西有什麼用呢?我認爲上面的模型基本上抽象出了咱們要解決問題的框架,咱們在接到一個新系統的需求或者一個新的功能需求時,能夠先構思一下有哪些模塊,模塊之間是否有通訊,須要在什麼時機進行通訊,多個模塊之間是否有公用代碼。針對每一個模塊,須要想一下模塊是否須要初始化數據和重置數據,模塊內部的用戶事件有哪些,獲取數據的邏輯和時機如何,模塊須要記錄的狀態有哪些。作了這些思考以後咱們對系統的複雜度就有了一個大體認識,對於評估工做量和作排期計劃都是有幫助的,更重要的是若是不提早想好這些重要的細節咱們可能會在開發的後期才發現他們,他們甚至可能一直被忽略於是引發線上 Bug,模塊的重置和數據請求的時序邏輯是常見的容易被忽略的問題。
除此以外,咱們能夠根據模型給出一些典型的功能場景,而後對比用不一樣技術框架來實現功能時的優缺點,這樣便於咱們對比具體的代碼,而不是憑空地爭論。
最後,但願這些思考可以拋磚引玉,歡迎你們批評指正。