redux 的安裝
npm install redux --save
redux 有五個(gè)js構(gòu)成,純函數(shù)實(shí)現(xiàn)
因?yàn)?redux 是純函數(shù),所以很方便進(jìn)行測(cè)試.在webStorm新建個(gè)node環(huán)境,安裝redux,就可以跑redux的例子.
先看個(gè)最簡(jiǎn)單的使用例子:
/**
* Created by lijie on 16/8/7.
*/
// 首先定義一個(gè)改變數(shù)據(jù)的plain函數(shù)快集,成為reducer
'use strict';
function count (state, action) {
var defaultState = {
year: 2015,
};
state = state || defaultState;
switch (action.type) {
case 'add':
return {
year: state.year + 1
};
case 'sub':
return {
year: state.year - 1
}
default :
return state;
}
}
// store的創(chuàng)建
var createStore = require('redux').createStore;
var store = createStore(count);
// store里面的數(shù)據(jù)發(fā)生改變時(shí)炮叶,觸發(fā)的回調(diào)函數(shù)
store.subscribe(function () {
console.log('the year is: ', store.getState().year);
});
// action: 觸發(fā)state改變的唯一方法(按照redux的設(shè)計(jì)思路)
var action1 = { type: 'add' };
var action2 = { type: 'add' };
var action3 = { type: 'sub' };
// 改變store里面的方法
store.dispatch(action1); // 'the year is: 2016
store.dispatch(action2); // 'the year is: 2017
store.dispatch(action3); // 'the year is: 2016
運(yùn)行結(jié)果如下:
該例子只用到了一個(gè)API: createStore, 先看這個(gè)主干 createStore.js
createStore.js
參數(shù)
function createStore(reducer, initialState, enhancer) {
...
}
reducer 示例中的counter
函數(shù),作用是根據(jù)action處理state的變化
initialState 初始化的 state 狀態(tài)樹
enhancer 增強(qiáng)函數(shù),需要用applyMiddleware
函數(shù)加入自定義的功能.
例如 常用第三方庫redux-thunk
與redux-logger
subscribe 函數(shù)
function subscribe(listener) {
if (typeof listener !== 'function') {
throw new Error('Expected listener to be a function.');
}
var isSubscribed = true;
ensureCanMutateNextListeners();
nextListeners.push(listener);
return function unsubscribe() {
if (!isSubscribed) {
return;
}
isSubscribed = false;
ensureCanMutateNextListeners();
var index = nextListeners.indexOf(listener);
nextListeners.splice(index, 1);
};
}
listener 表示觀察者函數(shù)
nextListeners: 最新的listener 數(shù)組
該函數(shù)在做的事情: 將之前的listener數(shù)組復(fù)制給nextListener,并將當(dāng)前的注冊(cè)的listener也加入nextListener中
返回的是 清除當(dāng)前觀察者對(duì)象的函數(shù) ,該函數(shù)就將當(dāng)前l(fā)istener從數(shù)組中又拿掉了.以后再通過dispatch發(fā)送action時(shí),該listener將不會(huì)通知該函數(shù)了.
dispatch 函數(shù)
function dispatch(action) {
if (!(0, _isPlainObject2["default"])(action)) {
throw new Error('Actions must be plain objects. ' + 'Use custom middleware for async actions.');
}
if (typeof action.type === 'undefined') {
throw new Error('Actions may not have an undefined "type" property. ' + 'Have you misspelled a constant?');
}
if (isDispatching) {
throw new Error('Reducers may not dispatch actions.');
}
try {
isDispatching = true;
currentState = currentReducer(currentState, action);
} finally {
isDispatching = false;
}
var listeners = currentListeners = nextListeners;
for (var i = 0; i < listeners.length; i++) {
listeners[i]();
}
return action;
}
前面一長(zhǎng)串方法都是判斷發(fā)送的Action是否是 對(duì)象,actionType是否是 undefined ,是否已經(jīng)發(fā)送過
最關(guān)鍵部分 currentState = currentReducer(currentState, action);
currentReducer 就是初始化賦值給的reducer函數(shù)
在dispatch 函數(shù)中給到各個(gè)reducer進(jìn)行處理
最后一步 for 循環(huán)通知當(dāng)前所有的觀察者
這樣的話,示例中所運(yùn)行的效果就能解釋通了.
redux 中間件
redux 庫能衍生出豐富的工具集和可拓展的生態(tài)系統(tǒng)
為剛才的示例添加一個(gè) 監(jiān)控?cái)?shù)據(jù)變化的 logger 的中間件
'use strict';
const logger = store => next => action => {
console.log("pre---"+JSON.stringify(store.getState()));
const result = next(action);
console.log("end---"+JSON.stringify(store.getState()));
return result;
};
function count (state, action) {
var defaultState = {
year: 2015,
};
state = state || defaultState;
switch (action.type) {
case 'add':
return {
year: state.year + 1
};
case 'sub':
return {
year: state.year - 1
}
default :
return state;
}
}
// store的創(chuàng)建
var createStore = require('redux').createStore;
var applyMiddleware=require('redux').applyMiddleware;
const createStoreWithMiddleware = applyMiddleware(logger)(createStore);
var store=createStoreWithMiddleware(count,undefined);
// store里面的數(shù)據(jù)發(fā)生改變時(shí)唉侄,觸發(fā)的回調(diào)函數(shù)
store.subscribe(function () {
console.log('the year is: ', store.getState().year);
});
// action: 觸發(fā)state改變的唯一方法(按照redux的設(shè)計(jì)思路)
var action1 = { type: 'add' };
// // 改變store里面的方法
store.dispatch(action1); // 'the year is: 2016
運(yùn)行結(jié)果如下:
實(shí)現(xiàn)該功能 主要用到了applyMiddleware.js
的API
applyMiddleware.js
function applyMiddleware() {
//在這里 找到所有的 第三方中間件 函數(shù)
for (var _len = arguments.length, middlewares = Array(_len), _key = 0; _key < _len; _key++) {
middlewares[_key] = arguments[_key];
}
return function (createStore) {
return function (reducer, initialState, enhancer) {
//創(chuàng)建store
var store = createStore(reducer, initialState, enhancer);
var _dispatch = store.dispatch; //拿到store的dispatch函數(shù)
var chain = [];
var middlewareAPI = {
getState: store.getState,
dispatch: function dispatch(action) {
return _dispatch(action);
}
};
//將middlewareAPI 作為第三方中間件 參數(shù),并返回 該所有第三方中間件的函數(shù)數(shù)組
chain = middlewares.map(function (middleware) {
return middleware(middlewareAPI);
});
//從右到左, middleware1( middleware2( middleware3(dispatch) ) )
_dispatch = _compose2["default"].apply(undefined, chain)(store.dispatch);
return _extends({}, store, {
dispatch: _dispatch
});
};
};
}
該段代碼的作用: 創(chuàng)建store,將中間件轉(zhuǎn)成數(shù)組,將store的state與dispatch給到中間件,并嵌套執(zhí)行.
在這里面有個(gè)compose.js
的api使用.官方解釋作用是 (...args) => f(g(h(...args))).
將數(shù)組依次嵌套執(zhí)行.
compose.js
為了更清楚理解compose.js
的作用,我們?cè)偬砑右粋€(gè) 第三方中間件.
'use strict';
const logger = store => next => action => {
console.log("pre---"+JSON.stringify(store.getState()));
const result = next(action);
console.log("end---"+JSON.stringify(store.getState()));
return result;
};
/**
* 用 { meta: { delay: N } } 來讓 action 延遲 N 毫秒箩溃。
* 在這個(gè)案例中,讓 `dispatch` 返回一個(gè)取消 timeout 的函數(shù)栋豫。
*/
const timeoutScheduler = store => next => action => {
if (!action.meta || !action.meta.delay) {
return next(action)
}
let timeoutId = setTimeout(
() => next(action),
action.meta.delay
)
return function cancel() {
clearTimeout(timeoutId)
}
}
function count (state, action) {
var defaultState = {
year: 2015,
};
state = state || defaultState;
switch (action.type) {
case 'add':
return {
year: state.year + 1
};
case 'sub':
return {
year: state.year - 1
}
default :
return state;
}
}
// store的創(chuàng)建
var createStore = require('redux').createStore;
var applyMiddleware=require('redux').applyMiddleware;
const createStoreWithMiddleware = applyMiddleware(logger,timeoutScheduler)(createStore);
var store=createStoreWithMiddleware(count,undefined);
// store里面的數(shù)據(jù)發(fā)生改變時(shí)晌端,觸發(fā)的回調(diào)函數(shù)
store.subscribe(function () {
console.log('the year is: ', store.getState().year);
});
// action: 觸發(fā)state改變的唯一方法(按照redux的設(shè)計(jì)思路)
var action1 = { type: 'add', meta: { delay: 2000 }};
// // 改變store里面的方法
store.dispatch(action1); // 'the year is: 2016
添加的中間件 作用是發(fā)送帶timeout的action
.運(yùn)行代碼,你會(huì)發(fā)現(xiàn)結(jié)果并沒有變化,只是listener 得到消息的時(shí)間推遲了2000毫秒,但不同的是,logger是實(shí)時(shí)性的.
所以compose.js讓中間件的執(zhí)行順序是 timeoutScheduler( logger(dispatch) )
的.
把使用的順序顛倒一下:
const createStoreWithMiddleware = applyMiddleware(timeoutScheduler,logger)(createStore);
將示例的代碼改成這樣. 你會(huì)發(fā)現(xiàn) logger 執(zhí)行順序變慢了2000毫秒.
因?yàn)楝F(xiàn)在的執(zhí)行是: logger( timeoutScheduler(dispatch) )
所以可以得出結(jié)論: compose.js 中執(zhí)行順序是 數(shù)組中排在前面的方法最先執(zhí)行,執(zhí)行完畢后將 dispatch函數(shù) 傳遞給后一個(gè)執(zhí)行. 并且在前面執(zhí)行的中間件將影響其后的中間件.
相關(guān)鏈接
官方gitbook: http://cn.redux.js.org/docs/advanced/Middleware.html
解讀redux工作原理: http://blog.csdn.net/jhqdlove/article/details/51940400