如何看待 Immutable App Architecture?

- Dan Abramov 在 ReactEurope 2016 中說起 redux 的未來,說很可能在未來 redux 只是這個架構下的其中一個環節,並說這個架構能得到更好的用戶與開發者體驗

- 相關演講視頻應該是這個 https://vimeo.com/166790294


我沒有全部看完,最後的2-3分鐘的內容沒往下看,中間的乾貨是基本上認真看了,憑著記憶說幾點

第一,關於經典MVC模型的缺點,他說的沒錯,但黑得有點厲害了

就是這張圖,能把MVC玩成這樣的人,去玩你的Redux或者說Immutable App Archtecture能玩得漂亮?

第二,對於整個邏輯循環的部分

就是這張圖,我不能苟同

在這個圖裡有個比較麻煩的問題,就是State的變化入口有2個,一個來自Action,一個來自Server,這就會導致從「View不符合預期」反查問題的時候,找到State變化之後發現入口太多而不好進一步判斷

我認為正確的方式應該由Server生成Action,所有的State變化都必須由Action造成,且Action是同步的

當然這也可能是我沒理解演講者的意思

第三,Optimistic UI部分給我的啟發很大

對於原本就有Redux或者Immutable實施經驗的人來說,估計全PPT唯一有價值的就是這一頁了……

但這裡有個問題,生成Optimistic State的Action和生成True State的Action真的可以是同一個嗎?

------

然後再說些自己的想法,會比較零亂

  1. 基本上演講者提的內容,和我現階段對UI的設想是相同的,強制性的引入Immutable,純通過state生成UI,組件本身不具備內部的狀態,除了Optimistic UI以外,其它方面我在做組件系統規劃的時候也基本涵蓋了(沒見過臉皮這麼厚的人)。具體我也和一些人有過討論,但最近的工作狀態是沒辦法實施落地的:一種更純粹的組件實現方式 · Issue #2 · ecomfe/efe · GitHub

  2. Optimistic UI這一塊確實說的好,但實際實施絕對不是這麼容易的。參考上圖,事實上在Optimistic State和最後Action的實際State之間一定會存在一些差異,這些差異可能導致界面比較劇烈的變化,這會給用戶造成不小的困惑(明明說成功了,怎麼現在界面又變了?)。因此,Optimisitc UI我多見於信息流類應用中,其它類應用並不見得玩得轉
  3. 演講者主要講了Immutable在架構上的意義,從一個實際寫代碼的人的角度出發,我只能說Immutable帶給我的是如加長防側漏般的「安全感」,這種感覺沒有實際大量Immutable的實踐是很難言傳的,我也不展開說了
  4. Immutable.js是好東西,但是對於數據結構進行wrap一直是我反對的,所以我還是會繼續玩我自己的一套基於原生對象的immutable方案(非seamless-immutable)
  5. Immutable的落地是一個很大的事,大部分情況下,一個已經存在的系統是沒辦法局部Immutable改造的,因為狀態的共享是千絲萬縷的關係,而Immutable架構下只要有哪怕一行代碼是Mutable的,就會千里之堤潰於蟻穴,而且出了問題你還追查不到根源,到時候哭吧

  6. 但是Immutable對於局部代碼(比如一個函數內部)的優化是很有用的,可以讓這一段邏輯更健壯,所以在實際編碼的時候很具有參考價值
  7. 我不看好當前國內的普遍技術成熟度下落地純函數組件為基礎的架構(雖然我很想這麼干),哪怕是技術實力在國內絕對名列前茅的我廠,其FE的能力也不足以駕馭純函數組件(老實說我廠大部分FE能不能駕馭聲明式組件都是問題)
  8. 在組件系統中,真正應該強調的不是「單向流」(話說這個流是數據流還是邏輯流大部分人都搞不懂),而是「單歸屬」,即一個組件它只能歸屬於一個owner,從這個owner獲取「數據」,也向這個owner發送Action/事件/回調,如果超過2個owner,整個系統的流絕對會混亂,上面某圖中state接收server和action就是這個問題

---

2016.09.19補充:

對於Optimistic UI部分的內容,我做了一些分析和研究,得出了一個應該更普適的方案:https://gist.github.com/otakustay/cc2625adeb1a37aca04f4f8f396a743f


我來拋個磚頭。

下圖是演講中所描述的 Immutable App Architecture 的概括圖。在通過實踐後,來講一講我對圖中的一些理解,以及它們能夠解決的問題。

1. Components → Views

推薦實現:

  • GitHub - facebook/react: A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • GitHub - facebook/react-native: A framework for building native apps with React.

Views代表的是各個平台提供的原生View組件(如Web中的DOM元素和iOS中的UIViews),並且這些View組件一般以樹的方式進行組合來渲染界面。通常我們創建一個View的方式為:

let e = createElement()
e.color = Colors.RED
other.addChild(e)

通過這樣的方式所產生的View樹可以繪製出完整的界面,但同時我們也必須通過許多引用來維護這個View樹。當Model(數據)發生變化時,我們就需要對這個View樹進行修改。為了完成對View樹的修改,我們書寫代碼的邏輯則通常包括:判斷Model的改變引起了哪一部分View的改變,然後對這些View直接進行修改,而被修改的這些View又可能引起Model的改變,Model的改變有可能引起新的Views的變化……。

所以我覺得上圖的情況在複雜需求的情況下是很難很難避免的(至少我很難從代碼中直接判斷哪些Model的變化會引起某個特定View的變化)。此外,如果直接對View進行修改的話,在優化以及reconcilement方面也很難實現,因此渲染新界面的速度並不理想。

為了避免這種因為「改變」而進行「改變」的死循環,架構中使用了Component(組件)來解決這個問題。組件定義了什麼樣的數據能夠繪製出什麼樣的View樹(State =&> View),因此只需要關心Component將會收到什麼樣的數據,以及這些數據應該繪製出怎樣的View樹。Component是一個pure function(純函數),相同的輸入總是得到相同的輸出,具有immutability(不可修改性),不管從實現或debug方面都變得更加簡單。

當然,Component的內容遠遠不止這些,具體可以參考react.js。

2. Models → Components

推薦實現:

  • GitHub - facebook/immutable-js: Immutable persistent data collections for Javascript which increase efficiency and simplicity.

Model代表的就是數據,可以是任意的實現,只需要Component能夠處理就ok。我推薦使用immutable.js來作為Model在Web上的實現。immutable.js所產生的對象,對其(A對象)的任何修改操作將操作結果作為一個新的對象(B對象)返回,而原有的對象(A對象)將不會發生任何改變。

由此帶來的好處在於,當一個Component接受到這個Model時,在繪製的過程中可以保證Model永遠不會被未知的或是不小心的操作進行修改。重要的是,這個Component也無法通過自己擁有Model來對其他的Model進行修改(我的經驗是,如果一個Model被擁有越多的潛在引用或是顯性引用,就越可能會被未知的操作進行修改,從而導致bug的出現,一般這種bug我一般找的很痛苦)。immutable.js解決了耦合性的問題,並且它對Model實現帶來的效率降低很微小(從此以後我就沒遇到過這種bug了,真的)。

3. Views → Actions, State(唯一性) → Models

推薦實現:

  • GitHub - reactjs/redux: Predictable state container for JavaScript apps

概括來說,我們對View的操作會產生不同的Action,而這些這些Action會引起State的改變,State的改變會產生不同的Model,Component在收到不同的Model就可以繪製出不同的View。這一過程是不可逆的,View的改變是由於產生Action引起(來自用戶的操作或伺服器)的,並且而View的改變是不會產生Action的。這樣單向數據流因此可以大大減小開發中的複雜性,也能夠更快地發現bug出現在哪個地方。

題主提到的redux就主要提供了:

  1. 提供了reducer(State, Action =&> State)。reducer也是一個pure function,接受到相同的Action時,總會對State完成相同的改變,不會受到任何的其他影響。
  2. 將State與Component綁定。當State發生改變時,會及時通過產生新的Model來更新相應的Component,即保證了Component與State的同步。
  3. 提供了各種工具來方便使用與配置redux。

4. Actions → Queue, Queue → State(唯一性)

在演講中使用了一個例子來解釋了optimistic state,

當創建一個todo時,如果已知了它的title,但需要將它提交至伺服器才能獲得到它的id是什麼。

  1. 我們可以將這個todo立刻添加到State中(ActionA),View立刻顯示了新的todo的title,用戶可以很快的得到反饋而不需要等待伺服器返回創建成功的返回。
  2. 此時我們還不知道這個新的Tode的id,但如果用戶接下來對這個新產生的todo進行了刪除操作產生了ActionB。而ActionB需要等待ActionA真正完成返回todo的id才能向伺服器發送刪除的請求繼續進行。此時我們可以將這個todo從State中立刻移除,用戶可以很快的得到反饋而不需要等待伺服器返回刪除成功的返回。

在開發過程中,延遲的大小和請求失敗的成功與否都是不確定的,而Action的並發產生頻率很高時,optimistic state就能夠完成很多邏輯。

通過使用optimistic state,能夠快速地對用戶的操作進行反饋,並且當伺服器返回真正的數據時能夠完成符合邏輯的更新,通過Queue來省去了複雜邏輯的實現。例如:當ActionA失敗時,ActionB不應該執行,todo也不應當出現在View中;當ActionA成功而ActionB失敗時,todo獲得了自己的id而刪除操作也應該被撤銷。

個人覺得對Immutable App Architecture理解是:

  • 高內聚低耦合,在編寫每一模塊的代碼時,其他模塊很難能夠對它造成影響。

  • 能夠適應與用戶或者伺服器交互性比較強的項目。
  • 對渲染速度有一點要求的項目。
  • 開發速度會慢一些,但維護性與修改性會強很多,debug起來也很方便。
  • 開發過程會比較麻煩,但配合各種奇技淫巧或配合團隊開發習慣的工具來開發會輕鬆很多。


推薦閱讀:

asy?
如果將來想從事web前端開發,而我專業並不是學的這個,需要做哪些準備?
用純HTML 5+CSS3的方法,如何控制Video的視頻與Poster的海報圖片尺寸一致(非視頻原尺寸,比例鎖定)?
web前端開發者應如何應對PC端的高PPI?
Web 前端開發有哪些痛點?各大公司是如何解決或緩解的?

TAG:前端開發 | Redux |