Flux是Facebook用來構建用戶端的Web應用程序的體系架構,與其它形式化的框架相比,它更像是一個架構思想,用於管理和控制應用中數據的流向。這裏應用中的數據指包括但不限於來自服務端的數據頁面中view的一些狀態(如一個面板是展開仍是關閉),臨時存儲在本地須要持久化到服務端的數據等。javascript
好了,說了這麼多好像仍是一臉懵逼,不慌,接下來看看展開式。css
在講述Flux以前,咱們看看以前傳統的MVC架構以及在前端中的一些問題繼而思考Flux帶來的改變。MVC(Model-View-Controller)最早興起於後端,經過對應用程序複雜度的簡化使程序更加直觀和便於維護。後端程序MVC中View能夠看爲數據的呈現,Model爲數據的模型,Controller做爲程序的流程控制。如今假設有這樣的場景,用戶想查看本身的profile頁面,可能會有這樣的流程:在頁面上點擊profile按鈕,接下來就是一個HTTP請求(/profile?username=jiavan) => Controller接收到這一請求並得到請求的內容username=jiavan而後告知Model須要jiavan的數據 => Model返回了jiavan的數據 => Controller獲得數據返回新的視圖,看下流程:html
如今前端中又有這樣的場景:切換Menu中的Item,當前選中的Item顏色不一樣於其它顏色而且底部顯示對應Item的內容。通常狀況下咱們會定義一個css class來做爲當前選中Item的樣式。當用戶點擊Item_A爲被點擊的元素新增高亮的class,其它兄弟元素移除該樣式,這裏的事件響應函數就是Controller,咱們會在這裏處理樣式修改邏輯,以及更新Model的數據,而後新的數據及樣式從新渲染界面。這種VC<->M
的形式在關係比較簡單的狀況下是比較清晰容易控制的,可是複雜的頁面上這樣的模式可能會變得很是混亂:前端
之因此變得混亂了,由於不少view都具有修改多個model的能力,這裏的單個修改行爲能夠稱之爲一個Action,一個Action的產生多是用戶行爲,或者一個Ajax請求須要渲染新界面。對比上面後端傳統MVC模式能夠發現:java
那麼是否是能夠把前端中修改狀態即state的行爲(事件回調/Ajax)所有抽象成一種Action描述,而後交付到一處即Reducers來進行原子化處理,而後Reducer修改整個應用中惟一的一棵state tree即Store,最後經過state->view的機制來從新渲染?react
上面提到的幾個概念已經對Flux有了初步的瞭解,下面進入正題。相信有了解Flux的都應該看過下面這張著名的數據流圖:git
因此Flux能夠被看做傳統MVC的改進而非顛覆,當我第一次看到Flux的時候實際上是比較懵逼,但看到並使用了Redux後確實有一種很是驚豔的感受。github
按照Redux官方的描述Redux is a predictable state container for JavaScript apps.
,其中predictable
和state container
體現了它的做用。那麼如何來理解可預測化
的呢?這裏會有一些函數式編程方面的思想,在Redux中reducer函數是一個純函數,相同輸入必定會是一致的輸出,因此肯定輸入的state那麼reducer函數輸出的state必定是能夠被預測的,由於它只會進行單純的計算,保證正確的輸出。狀態容器
又是什麼?說明Redux有一個專門管理state的地方,就是Store,而且通常狀況下是惟一的,應用中全部state造成的一顆狀態樹就是Store。Redux由Flux演變而來,但受 Elm 的啓發,避開了 Flux 的複雜性,咱們看看其數據流向:編程
不一樣於Flux架構,Redux中沒有dispatcher這個概念,而且Redux設想你永遠不會變更你的數據,你應該在reducer中返回新的對象來做爲應用的新狀態。可是它們均可以用(state, action) => newState
來表述其核心思想,因此Redux能夠被當作是Flux思想的一種實現,可是在細節上會有一些差別。redux
一個object tree
的形式存儲在一個單一的store中;動做行爲的抽象
;這裏須要說明一點的是reducer函數,它應當是一個純函數,不該該有反作用,不該有API調用,Date.now()
或者隨機獲取等不穩定的操做,應當保證相同的輸入reducer計算的結果應該是一致的輸出,它只會進行單純的計算。編寫reducer函數也是Redux中比較重要的一塊,它的形式以下:
function testReducer(state, action) { switch (action.type) { case ACTION_TYPE: // calc... return newState; default: return state; } return newState; }
state是不可修改的,因此返回的新state應該是基於輸入state副本的修改,而不是直接修改state後的返回。
1. 單一數據源,store
整個應用的state被存放在一棵Object tree中,而且這個Object tree只存在惟一一個store中;
2. state是隻讀的
惟一能改變state的方法是觸發action,action是對已經發生了的事情的抽象描述,簡單的講,它把行爲抽象成了一個對象。
好比,刪除一條記錄的action能夠抽象的理解爲:
{ type: 'DELETE_ITEM', index: 1, }
3. 使用純函數來實現state歸併操做,reducer
傳入待修改的state和一個告知reducer如何修改state的action,reducer將返回action規則對應下操做後的新的state。
reducer(state, action) => new state
嚴格的單向數據流是Redux設計的核心
Redux應用數據的生命週期遵循下面4個步驟:
新的state樹就是應用的下一個狀態
,如今就能夠根據新的state tree來渲染UI。
咱們經過一個很是簡單的計數器demo來梳理Redux的數據流。
0x00. 建立action
action其實就是一個普通的對象,只是對行爲的抽象描述,這裏咱們能夠把加上一個數描述爲:
{ type: INCREMENT, //該動做的抽象描述 number, // 該動做攜帶的數據 }
更多的時候咱們會經過一個action生成函數來獲得一個action:
function incrementCreator(number) { return { type: INCREMENT, number, }; }
0x01. 建立reducer函數
reducer做爲整個Redux中action的處理中樞,接收state與action並對此修改數據,返回應用的下一個狀態。
function countReducer(state, action) { switch (action.type) { case INCREMENT: return Object.assign({}, { counter: state.counter + action.number, }); case DECREMENT: return Object.assign({}, { counter: state.counter - action.number, }); default: return state; } }
注意:上面咱們已經提到屢次,state是不可修改的,因此經過assign
歸併咱們對數據的操做,返回的是state副本修改後的對象,並不是直接修改了輸入的state。
0x02. 建立惟一store
經過Redux中的createStore方法傳入reducer函數來建立整個應用的store。
const store = createStore(countReducer);
0x03. 修改state
經過store的dispatch方法來發起一個action。
store.dispatch(incrementCreator(5)); store.dispatch(decrementCreator(4));
import { createStore } from 'redux'; // actions const INCREMENT = 'INCREMENT'; const DECREMENT = 'DECREMENT'; // actionCreator,能夠視爲建立action的語法糖 function incrementCreator(number) { return { type: INCREMENT, number, }; } function decrementCreator(number) { return { type: DECREMENT, number, }; } // 初始化state const initialState = { counter: 0, }; // reducers函數,注意最後必定要return state防止不能匹配到action的時候state丟失 function countReducer(state = initialState, action) { switch (action.type) { case INCREMENT: return Object.assign({}, { counter: state.counter + action.number, }); case DECREMENT: return Object.assign({}, { counter: state.counter - action.number, }); default: return state; } } // 建立store const store = createStore(countReducer); // 訂閱store的修改 const unsubscribe = store.subscribe(function log() { console.log(store.getState()); }); // 經過dispatch action來改變state store.dispatch(incrementCreator(5)); //Object {counter: 5} store.dispatch(decrementCreator(4)); //Object {counter: 1} // 取消訂閱 unsubscribe();
原文出處 https://github.com/Jiavan/jia... 以爲對你有幫助就給個star吧