- 什麼是Redux中介軟體
- 存在中介軟體時倉庫的初始化
- 建立一箇中介軟體
- applyMiddleware.js
- compose.js
- thunk
- promise
pre-notify
previously:Redux:全揭祕與入坑指北(上)
上一篇說了下creatStore和react-redux,這一篇主要說一下中介軟體
本來想畫全流程解析圖的,emmm...沒找到好的工具,可以參考下網上流傳的幾張
什麼是Redux中介軟體
中介軟體是插入在使用者發射action
動作之後到reducer
接收到這個動作之前這個時機的處理器,它能完成一些額外的邏輯。
存在中介軟體時倉庫的初始化
平時我們是這麼初始化一個有中介軟體的倉庫的
import {createStore,applyMiddleware} from 'redux';
let store = createStore(reducer,{astate:..,bstate:...,...},applyMiddleware(xx,yy,zz));
複製程式碼
其實內部是這樣呼叫的
let store = applyMiddleware(xxx,yyy,...)(createStore)(reducer,{astate:..,bstate:...,...})
複製程式碼
emmm,原理是這樣的
export default function createStore(reducer, preloadedState, enhancer){ // 增強器
if(enhancer && typeof enhancer === 'function'){
return enhancer(createStore)(reducer,preloadedState)
}
...
複製程式碼
So,我們現在知道了,如果有中介軟體,會先執行applyMiddleware
應用中介軟體這個方法,並且將createStore
、reducer
、preloadedState
傳到applyMiddleware這個方法裡面,在裡面初始化倉庫。
建立一箇中介軟體
我們再來看看平時我們是怎麼定義一箇中介軟體的
let logger = ({dispatch,getState})=>next=>action=>{
//do shome thing else
console.log(1);
next(action);
console.log(11);
//do some thing else agin
}
複製程式碼
它等價於
let logger = function({dispatch,getState}){
return function(next){
return function(action){
//do shome thing else
console.log(1);
next(action);
console.log(11);
//do some thing else agin
}
}
}
複製程式碼
其中,getState
就是createStore
初始化一個倉庫時原本匯出的getState,action
也還是使用者所發射的那個reducer所接受的那個action,
但dispatch
不再是createStore所匯出的那個了,而是經過中介軟體包裝後的dispatch。
另外next
其實是另外一箇中介軟體,和Koa中的中介軟體一樣我們需要手動呼叫next來讓佇列中的中介軟體一次執行。並且中介軟體的執行及結果依然遵循洋蔥模型。
我們來看這樣一個栗子,假若我們有兩個中介軟體
//中介軟體1
let middle1 = function({dispatch,getState}){
return function(next){
return function(action){
//do shome thing else
console.log(1);
next(action);
console.log(11);
//do some thing else agin
}
}
}
//中介軟體2
let middle2 = function({dispatch,getState}){
return function(next){
return function(action){
//do shome thing else
console.log(2);
next(action);
console.log(22);
//do some thing else agin
}
}
}
複製程式碼
然後我們這樣初始化倉庫時這樣註冊他們
let store = applyMiddleware(middle2,middle1)(createStore)(reducer);
複製程式碼
那麼當我們發射一個動作時,比如說
<button onClick={this.props.increment}>+</button>
複製程式碼
那麼此時就會呼叫派發,只不過此時的派發方法是經過先經過middle1
包裝後又經過middle2
包裝的方法。它會先執行middler2
額外新增的一部分程式碼,然後再執行middle1
額外新增的一部分程式碼,然後才會真正的派發,派發完以後又會將middle1
剩下的程式碼執行完,最後是middle2
剩下的程式碼。
執行結果會是:先輸出2,再輸出1,再執行store.dispatch,再輸出11,最後輸出22
接下來,我們來看這樣的模型具體是怎樣實現的。
applyMiddleware.js
這個方法最後會將原本的store.dispatch
給替換成新的dispatch,這個新的dispatch是則是我們上面所說的經過中介軟體層層包裝後的新的dispatch。
//applyMiddleware.js
export default function(...middlewares){
return function(createStore){
return function(reducer,preloadedState){
let store = createStore(reducer,preloadedState);
let dispatch;
let middlewareAPI = {
getState:store.getState
,dispatch:action=>dispatch(action)
}
middlewares = middlewares.map(middleware(middlewareAPI));
dispatch = compose(...middlewares)(store.dispatch);
return {...store,dispatch};
}
}
}
複製程式碼
compose.js
在applyMiddleware.js
中我們用到了一個compose
方法,
這個方法會把中介軟體序列起來,並且一層包一層,按照applyMiddleware()
呼叫時中介軟體註冊的順序,先註冊的會成為洋蔥模型最外面的一層,後註冊的則往裡一層,最裡面是原本的store.dispatch。
//compose.js
export default function(...fns){
return function(...args){
let last = fns.pop();
return fns.reduceRight((val,fn)=>{
return fn(val);
},last(...args));
}
}
//--- --- ---
//高逼格版
export default function(...fns){
if(fns.length === 1)return fns[0];
return fns.reduce((a,b)=>(...args)=>a(b(...args)));
}
複製程式碼
為了便於理解compose
函式的作用,請看一下以下的例子
function add1(str){
return str+'com';
}
function add2(str){
return str+'po';
}
function add3(str){
return str+'se!';
}
add3(add2(add1('hello,'))); //輸出:hello,compose!
//以上等價於
let add = compose(add3,add2,add1);
add('hello,');
複製程式碼
So,上面的add
其實就是我們的中介軟體
thunk
...
,thunkIncrement(){
return function(dispatch,getState){
setTimeout(function(){
dispatch({type:types.INCREMENT,payload:1})
},1000);
}
}
...
複製程式碼
//處理自定義非同步操作的中介軟體
let thunk = ({dispatch,getState})=>next=>action=>{ //處理函式的
if(typeof action == 'function'){
action(dispatch,getState); //說明是一個非同步動作,將dispatch等傳給這個非同步動作的函式,在非同步有結果後再執行派發改變原本狀態。
}else{
next(action); //匹配下一個中介軟體
}
};
複製程式碼
promise
...
//1)
,promiseIncrement(){
return new Promise(function(resolve,reject){
setTimeout(function(){
resolve({type:types.INCREMENT,payload:1}); //reject無法處理
},1000)
});
}
//2)
,payloadIncrement(){
return {
type:types.INCREMENT
,payload:new Promise(function(resolve,reject){
setTimeout(function(){
if(Math.random()>.5){
resolve(100);
}else{
reject(-100);
}
},1000)
})
}
}
...
複製程式碼
//處理promise的中介軟體
let promise = ({dispatch,getState})=>next=>action=>{
if(action.then&&typeof action.then === 'function'){ //是一個promise
action.then(dispatch);
}else if(action.payload&&action.payload.then){
action.payload.then(payload=>dispatch({...action,payload}),payload=>dispatch({...action,payload}));
} else{
next(action);
}
};
複製程式碼