redux 單向資料流的由來
- Flux將應用分成四個部分;
- view 檢視層;
- Action 檢視層發出的訊息;(改變store裡面的資料)
- Dispatch(派發器)
- Store (資料層) : 用來存在應用的狀態(資料),一旦發生變動,就要提醒view更新頁面。
redux單向資料流:
具體詳情請見阮一峰Flux架構入門Action
-
定義. Action 是把資料從應用(譯者注:這裡之所以不叫 view 是因為這些資料有可能是伺服器響應,使用者輸入或其它非 view 的資料 )傳到 store 的有效載荷。它是 store 資料的唯一來源。一般來說你會通過 store.dispatch() 將 action 傳到 store。
-
狹義的Action
let action = {
type: 'ACTION_NAME',
...
}
複製程式碼
注意: 一般 type 的內容使用 大寫字母+下劃線 的格式.
-
廣義的Action
廣義的 action 是指在中介軟體的支援下,dispatch 函式可以呼叫的資料型別,除了普通action之外,常見的有 thunk, promise 等。我們用常用的 thunk來舉個例子
什麼叫thunk函式? 具體背景見阮一峰es6標準入門一書第17章Thunk函式的含義.在javaScript中函式將多引數函式替換成單引數的版本,且只接受回撥函式作為引數。 例如:
var Thunk = function(fn){
return function(){
var args = Array.prototype.slice.call(arguments);
return function(callback){
args.push(callback);
return fn.apply.(this,args);
}
}
}
var readFileThunk = Thunk(fs.readFile);
readFileThunk(fileA)(callback);
複製程式碼
Thunk函式版本的action:
複製程式碼
(dispatch, getState) => {
//在函式體內可以使用 dispatch 方法來發射其他 action
//在函式體內可以使用 getState 方法來獲取當前的state
}
複製程式碼
ceateStore
通過該API建立一個store物件,該物件包含四個方法;
- getState();獲取store中當前的狀態。
- dispatch(action): 分發一個action,並返回這個action,這是唯一能改變store中資料的方式。
- subscribe(listener): 註冊一個監聽者,它在store發生變化時被呼叫。
- replaceReducer(nextReducer): 更新當前store裡的reducer, 一般只會在開發模式中呼叫該方法。
redux middleware
-
Redux 是一個簡單的同步資料流,當分發一個action時,reducer收到action後,更新state並通知view重新渲染。 當action發出後如果想要執行一些別的操作,該怎樣處理,也就是說action發出後沒有立即執行reducer,將redux變成非同步. 這時就要藉助中介軟體。
-
redux-middleware的資料流動.
- 中介軟體的由來以及原理. 中介軟體的思想來源於koa. 核心思想:將middleware(函式)進行組合,將當前的middleware執行一遍作為引數傳給下一個middleware去執行。
原理:
app.use((ctx, next) => {
ctx.name = 'Lucy'
next()
})
app.use((ctx, next) => {
ctx.age = 12
next()
})
app.use((ctx, next) => {
console.log(`${ctx.name} is ${ctx.age} years old.`) // => Lucy is 12 years old.
next()
})
// ... 任意呼叫 use 插入中介軟體
app.go({}) // => 啟動執行,最後會呼叫 callback 列印 => { name: 'Lucy', age: 12 }
複製程式碼
ctx 引數就是 app.go 接受的物件。呼叫 app.go 其實會呼叫目標函式 app.callback,但是呼叫 app.callback 之前我們可以先讓引數 ctx 通過一系列的中介軟體,最後才會傳遞給 app.callback。
使用 app.use 插入任意中介軟體,中介軟體是一個函式,可以被傳入一個 ctx 和 next;呼叫 next 的時候會執行下一個中介軟體。如果不呼叫 next 會阻止接下來所有的中介軟體的執行,也不會執行 app.callback。
這裡的app.use()就是一個實現中介軟體。
const app = {
middleware:[],
callback(){
console.log(ctx);
},
use(fn){
this.middleware.push(fn);
},
go(){
const reducer = (next,fn,i)=> {
fn(ctx,next)
}
this.middleware.reduceRight(reducer,this.callback.bind(this,ctx))();
}
}
複製程式碼
- redux的applyMiddleware的原始碼.
function applyMiddle(){
(next) => (reducer, initialState) => {
let store = next(reducer,initialState);
let dispatch = store.dispatch;
let chain = [];
let middlewarAPI = {
getState:Store.getState,
dispatch: (action) => { dispatch(action)}
}
chain = middlewares.map(middleware => middleware(middlewarAPI));
dispatch = compose(...chain)(store.dispatch);
return {
...store,
dispatch
}
}
}
複製程式碼
一般這樣應用middleware
const finalCreateStore = compose(
applyMiddleware(...middleware)
//DevTools.instrument()
)(createStore);
const store = finalCreateStore(reducer);
複製程式碼
middleware的一般寫法
const m1 = store => next => action => {
let result = next(action);
switch (action.type) {
case APP_INCREMENT_LOADING:
globalProgressBar.incrementLoading();
break;
case APP_DECREMENT_LOADING:
globalProgressBar.decrementLoading();
break;
}
return result;
};
export default m1;
複製程式碼
注:這裡的compose函式請參考app.go或者參考上章FP一節; applyMiddle其實用了2箇中介軟體的思想; 原始碼的詳細解釋: