之前 review 業務代碼的時候就一直想說寫一篇自己對 flux 的了解和看法,不知不覺也過去蠻久了,于是這周末打起精神寫了這麼一篇。
另外文中沒有特别大段的代碼,以讨論架構設計和背後的道理為主,可能會顯得有點枯燥,大家可以選個不太困的時候耐心讀讀看:)
這是 flux 官方提供的一張說明圖:

圖中有四個名詞:
view
store
action
dispatcher
下面逐個以我的角度做個講解:
首先 view 是視圖,是使用者看得見摸得着的地方,同時也是産生主要使用者互動的地方,這個概念在 mvc 和 mvvm 架構中都是有的,有些觀點認為雖然這幾種架構裡都有 view,但是定義不太一緻,有細微的差别,我自己覺得這種差異确實是存在的,但在一開始這并不妨礙我們了解 view 這個名詞。
然後是 store,它對應我們傳統意義上的 data,和 mvc、mvvm 裡的 model 有一定對應關系。你問我它們為啥不直接叫 data 算了,那這就是文化人和小老百姓表達方式的差别。當然了我隻是想盡量降低了解成本,嘗試用比較通俗的說法把問題說清楚。
然後是 action,這看上去是一個新概念,實際上我還是能找到一些幫助大家了解的名詞,叫做 event。就是一個結構化的資訊,從一個地方傳遞到另一個地方,整個過程就是一個 action/event。
最後是 dispatcher,多說一句,我覺得正是因為有了 dispatcher 才讓前面三個名詞變得有新鮮感。也是了解 flux 的關鍵。言歸正傳,dispatcher 算是從 action 觸發到導緻 store 改變的鎮流器。比一般架構設計裡直接在“event”邏輯中修改“data”更“正規”。是以土得掉渣的 event 變成了 action,土得掉渣的 data 變成了 store,土得掉渣的 view 仍然是土得掉渣的 view。
因為“正規”
傳統 mvc 被 flux 團隊吐槽最深的,表面上是 controller 中心化不利于擴充,實際上是 controller 需要處理大量複雜的 event 導緻。這裡的 event 可能來自各個方向是 flux 吐槽的第二個點,是以不同的資料被不同方向的不同類型的 event 修改,資料和資料之間可能還有聯系,難免就會亂。
是以和 dispatcher 配合的 store 隻剩下了一個修改來源,和 dispatcher 配合的 action 隻剩下了約定好的有限幾種操作。一下子最混亂的地方變得異常“正規”了。架構複雜度自然就得到了有效的控制。
另外還有一個蠻好了解的點是:action 不僅僅把修改 store 的方式限制了起來,同時還加大了 store 操作的顆粒度,讓瑣碎的資料變更變得清晰有意義。
另外,這兩個地方抽象之後資料操作變得“無狀态”了,是以可以根據 action 的曆史記錄确定 store 的狀态。這個讓很多撤銷恢複管理等場景成為了可能。
綜上所述,在 flux 架構中,資料修改的顆粒度變大,更有語義;上層資料操作的行為更抽象化,碎片化程度降低。
不是,隻要在傳統架構的基礎上注重對資料操作和使用者/用戶端/伺服器行為的抽象定義,flux 架構中提到的各種好處大家都享受得到。
我們就拿被 flux 黑得最慘的那個“一大堆 v 和一大堆 m 隻有一個 c”的例子好了,圖中每個 view 找到不一樣 model 進行操作時,我們把這些操作抽象成 action,然後通過中心化的邏輯找到相應的 model 完成修改,其實就是 flux 了。這裡抽象出來的 action 一定要和圖中 controller 能夠接受到 action 一樣,沒有什麼特殊的地方。
基于這樣的了解,redux 提出了另外的對 flux 架構的了解:
首先 store 是通過 creator 建立出來的
每個 store 都有自己的 state 用來記錄目前狀态
在建立 store 的時候,通過 reducer 把 state 和 action 的關系建立起來
後期通過在 store 對象上 dispatch 不同的 action 達到對 state 的修改
本質上同樣是對資料操作和上層行為的抽象,另外從實作層面更加 functional。
vuex 是基于 vue.js 的架構設計,稍後再展開說我的看法。
(咳咳咳~~~ 這個問題我得謹慎回答)
我覺得 flux 架構沒有把一個事實告訴大家,就是它的 store 是中心化的,flux 用中心化的 store 取代了它吐槽的中心化的 controller。
我看了一些基于 flux/redux/vuex 架構的實作,基本上多個 store 之間完全解耦不建立任何聯系是不可能的——除非它們完全從資料行為各方面都是解耦的——這種程式用什麼架構都無所謂的坦白講。
為什麼中心化的 store 無人吐槽呢?因為中心化的資料複雜度絕對低于中心化的行為控制。你甚至沒有意識到它是中心化的,這其實從另外一個側面就證明了這一點。
是以我覺得透過 flux 看架構的本質:這裡不算是坑或吐槽,我更想說的是,放下 flux 這把錘子,我們該怎麼看世界,怎麼看待自己每天在設計和架構的軟體。
中心化管理資料,避免資料孤立,一旦資料被孤立,就需要通過其它程式做串聯,導緻複雜。這是避免各路行為亂改資料導緻混亂的一個潛在條件,或者說這是一個結論。
把行為做個歸納,抽象度提高,不管是使用者操作導緻的,還是從伺服器 pull 過來的,還是系統本身操作的。
把修改資料的操作做個歸納,顆粒度變大,大到純粹“無狀态”的極限。
另外一個沒有被過多談論的細節,就是從 model 到 view 要簡單直接,這一點各路架構都是有共識的,就不多說了。
在這幾個方面,如果一個架構師能夠做到極緻,去tm的各種架構縮寫,用哪個都一樣。
我先說我覺得 vue.js 怎麼樣,vue.js 天生做了幾件事:
components,即元件化,把視圖分解開
通過 computed options 簡化 data 到 template 的對應關系
通過 methods options 明确各路行為的抽象
通過雙向 computed options 增大了對 data 操作的顆粒度
部分 methods options 也可以用來完成純粹的 data 操作,增大對 data 操作的顆粒度
是以 vue.js 本身已經提供了很多很好的架構實踐。但這在 flux 看來還不夠純粹,它缺 2 點:
資料有 components 之間的樹形關聯,但是修改起來是分散的
相應的 computed、methods 也應該不是分散的,需要改造
是以 vuex 需要做的事情很簡單:
中心化的 store,所有 components 都共用一份資料,即一份 state;更複雜的情況下,定義有限的幾種 getters,用在 computed options 中
定義有限的幾種 mutations (類比從 dispatcher 到 store 的約定),可以直接用在 methods options 中;更複雜情況下,定義有限的幾種 actions (類比從各路行為到 dispatcher 的約定),用在 methods options 中,背後調用的是各種定義好的 mutations。
這樣在 vue 的基礎上,再加上如虎添翼的 vuex,開發者就可以享受到類似 flux 的感覺了。
是的,我覺得這是一個被用爛的詞,以至于很多人在求職面試的時候一被問到 flux 就脫口而出“單向資料流”,幾乎當做 flux 這個詞的中文翻譯在回答。就好像一說到 scrum 就脫口而出“看闆”一樣……
我覺得單向資料流的講法太過表面,不足夠展現出 flux 的設想和用意。現在一提單向資料流,我腦中第一個浮現的畫面其實是這個:
這是資料操作顆粒度變大之後的名詞。我覺得它隻是個名詞,為什麼這樣說?
所為“時空穿梭”,本質就是記錄下每一次資料修改,隻要每次修改都是無狀态的,那麼我們理論上就可以通過修改記錄還原之前任意時刻的資料。
大家設想一下,其實我們每次對資料最小顆粒度的、不能再分解的、最直接的操作基本 (比如指派、删除、增減資料項目等) 都是無狀态的,其實我們如果寫個簡單的程式,把每次直接修改資料的操作記錄下來,同樣可以很精細的進行“時空穿梭”,但沒有人提這個詞,因為它顆粒度太細了,沒有語義,沒有人願意在這樣瑣碎的資料操作中提煉“時空”。因為資料操作的顆粒度變大了,是以變得直覺,有語義,易于了解,對我們的功能研發和調試有實際幫助,是以才有了“時空穿梭”這個概念。
這是我最後想說的,首先不管有沒有 flux/vuex,一個好的架構實踐已經足以滿足日常的研發需求,尤其是在手機上,界面、資料和行為都不會特别複雜。
其次,如果基于 vue 2.0 來開發 weex 頁面或應用的話,vuex 是天生支援的,不需要額外做什麼。大家如果已經在浏覽器中,不論是桌面還是手機上實踐過 vuex,應該是感覺不到任何不一樣的。
最後,上周我簡單寫了個 vuex 的複刻版,能夠在 weex 的 js framework 上工作,這裡不想占太多篇幅介紹。坦白講我希望大家更多的精力在了解 flux 和 vue 上。其它問題都是順理成章的。
這篇文章整理了我個人對 flux 的了解和個人看法,首先解釋一下 flux 核心的四個名詞:view, store, action, dispatcher,然後提出 dispatcher 在 flux 架構中的關鍵位置,并解釋為什麼 dispatcher 讓其他三者變得更好更“正規”,然後是一些我通過了解 flux 認識到的背後倡導的架構設計的最佳實踐的提煉。
真的沒有代碼……
……好吧如果一定要看代碼可以看看這裡
<a href="https://github.com/reactjs/redux">https://github.com/reactjs/redux</a>
<a href="https://github.com/vuejs/vuex">https://github.com/vuejs/vuex</a>
<a href="https://github.com/jinjiang/weex-x">https://github.com/jinjiang/weex-x</a>
謝謝