跳到主要內容
黯羽輕揚每天積累一點點

Flux

免費2017-06-18#Front-End#Mind#React Flex#Flex思想#Flex指南#Flex优势#Flex图解

前端資料層

一. 定位

一種模式,用來強化單向資料流(unidirectional data flow)

二. 作用

剝離資料層,讓資料可預測(React 讓 UI 可預測,Flux 讓資料可預測)

具體做法:

  • 用顯式資料,不用衍生資料(先宣告後使用,不臨時造資料)

  • 分離資料和視圖狀態(把資料層抽出來)

  • 避免級聯更新帶來的級聯影響(M 與 V 之間互相影響,資料流不清楚)

作用:

  • 提升資料一致性

  • 易於精確定位 bug

  • 便於單元測試

三. 結構

         產生 action               傳遞 action           update state
view 互動 -----------> dispatcher -----------> stores --------------> views

其中 dispatcher 全域只有一個,store 可以有多個。dispatcher 只負責分發/傳遞 actionaction 到具體 state 變化之間的映射由 store 維護,所以 store 不是單純的狀態集 model,還包含根據 action 更新 state 的邏輯。再往後就是 stateview 的聯繫,與資料綁定的具體實現有關,比如 React 裡透過觸發事件來通知更新(隱式 setState()

業務邏輯大多在 store 裡,另一小部分互動相關的、非同步操作相關的在 view(比如 React 元件)裡

業務中經常有級聯更新,比如互動操作把一條訊息標為已讀,要更新訊息列表中該條訊息的展示樣式,還要把未讀訊息數量減一,級聯更新讓單向資料流變得不再清晰。Flux 透過約束必須在頂層觸發 action 來避免這種情況,一次 view 互動觸發一組 action(把級聯 action 打平,並把級聯關係收在頂層,與互動操作直接相關)。而不是一次 view 互動觸發一個大 action,大 action 觸發下面的級聯 action

store 來完成控制反轉,store 不提供 setXXX() 來允許外部影響內部 state,唯一的方式是透過在 dispatcher 上註冊的回呼拿到外部資料,自己更新內部 state,保持清楚的關注點分離

[caption id="attachment_1422" align="alignnone" width="625"]flux-simple-f8-diagram-explained flux-simple-f8-diagram-explained[/caption]

單 dispatcher

中心樞紐,所有資料流都要過這裡,有一張回呼註冊表,與各 store 建立聯繫。dispatcher 本身只負責把 action 傳遞給 所有 store,每個 storedispatcher 註冊自己並提供一個回呼,dispatcher 收到 action 後,所有已註冊的 store 都將透過各自的回呼拿到 action 及其攜帶的資料

應用規模較大的時候,dispatcher 會變得複雜一些,還要管理各 store 之間的 依賴關係(按順序呼叫各 store 註冊的回呼),store 可以在透過顯示宣告等待其它 store 更新完成後再更新自己

一堆 store

包含應用狀態和邏輯,角色相當於 MVC 裡的重 M

但管理一堆 state,而不像 ORM 裡 model 代表一條資料記錄,與 Backbone 裡的 collection 也不同,只是簡單地管理一組 ORM 風格的物件

一個 store 負責管理應用某塊功能對應的內部狀態,也就是說,store 不是按具體資料模型(ORM model),或者類型(Backbone collection)來分的,而是 按業務功能劃分。比如 ImageStore 負責記錄一組圖片的狀態,TodoStore 負責記錄一組 to-do item,這樣,store 在資料上表示 model 集,在邏輯上表示一塊單一功能

storedispatcher 上註冊的回呼接受一個 action 參數,store 裡面是一個 switch 語句,根據 actiontype 分發給具體 state 更新方法,store 更新完畢後,透過廣播事件來告訴 view 某些狀態變了,對應的 view 取新的狀態更新自己

一堆 view

一些特殊的 view 監聽來自自己依賴的 store 的廣播事件,這些 view 叫 controller-view,含有從 store 取資料及向向下傳遞給後代 view 的邏輯,一個 controller-view 通常對應頁面上的一塊邏輯內容,像 view 的邏輯分組一樣

controller-view 接到來自 store 的事件後,先透過 store 暴露的 getter 取新資料,然後呼叫自己的 setState() 或者 forceUpdate(),觸發 render()render() 觸發後代的 render()

通常把一大塊 state 向向下傳遞,下面各取所需,是為了減少需要管理的狀態(不作細粒度狀態切分)。相對於頂層 controller 從外部更新狀態,這樣能保持後代的功能儘量純淨

一堆 action

一般用工具方法來包裝 action 的生成、註冊到 store 的過程,內部維持 storeaction 的聯繫(透過 actiontype

action 也可能來自別處,比如伺服端,資料初始化時,服務傳回錯誤碼或者服務資料更新了,透過觸發 action 來同步視圖

四. 特點

強制同步

action 分發/傳遞和 store 內部更新 state 都是同步的,非同步操作的話,完成的時候手動觸發 action,整個機制不幫忙管理非同步操作

讓應用的資訊流非常明確,bug 場景對應的 state 向上追溯到 store,到對應 action,再到 view 層觸發 action 的點,過程中所有環節都是同步的,那麼 action 對應的 state 就是可預測的,不存在時序上的意外

控制反轉(IoC)

store 自己內部更新 state,而不是從外部更新,這樣其它部分都不需要知道具體的 state 變化,狀態變化只與 store 有關。而 store 只接收 action,想對 store 做單元測試的話,只需要給一個初態,再丟過來一個 action,然後看終態是否符合預期即可

語義化的 action

store 要根據 action 更新 state,這樣一個 action 就相當於一組 state 更新操作的名字,有了語義含義,action 不知道怎樣更新狀態,但描述了預期結果,是相對穩定的(很少需要修改 action,因為僅描述應用的某項功能),比如 MARK_THREAD_READ 希望把某條訊息置為已讀

額外的語義資訊有利於追蹤狀態變化,透過偵錯工具就能讓狀態變化可追蹤,比如 Redux DevTools

沒有級聯 action

不允許一個 action 觸發另一個 action,以避免級聯更新帶來的偵錯複雜度,所以 action 是「原子級」的,沒有複雜的層級關係

五. 約定

最佳實踐 部分,也就是 Flux 的 道德約束

store

  • 快取資料

  • 只暴露用來存取資料的 getter,不給 setter

  • 對來自 dispatcher 的特定 action 作出響應

  • 任何資料變化時都觸發 change 事件

  • 只在 dispatch 過程中才觸發 change 事件

維護內部狀態,且只在內部更新狀態,關注特定 action,資料變化時無理由觸發 change,其它時候不觸發,除非是 dispatcher 引發的

action

  • 描述用戶行為,而不是 setter(比如應該是 select-page 而不是 set-page-id

container

  • 用來控制 view 的 React 元件

  • 基本職能是收集來自 store 的資訊,存到自己的 state

  • 不含 props 和 UI 邏輯

其實就是 controller-view,與普通 view 的區別如上所述

view

  • 由 container 控制的 React 元件

  • 含有 UI 和渲染邏輯

  • 接收所有資訊和回呼作為 props

普通的 view,沒什麼特別的

參考資料

評論

暫無評論,快來發表你的看法吧

提交評論