在美團實習的時候,第一次接觸到dva這樣的react框架,回學校的時候,就想有機會本身實現一下這樣的框架,雖然本身水平有限,可是能夠試一試哈。 目標是實現dva model的同步和異步 dispatch action。react
看看 dva 的構成redux
let counterModel = {
namespace: 'counter',
state: {
num: 0
}
reducers: {
add(state, action){
return {
num: state.num + 1
}
}
}
}
複製代碼
對state的更新緩存
var app = new dva();
app.model(counterModel);
app.start();
app._store.dispatch({
type: 'counter/add'
});
複製代碼
上述就是 dva 對 state 的更新, 經過dispatch {type: A / B} 其中 A 是指 model的 namespace, B 是指 model 中 reducers 具體的reducer方法。app
其中 dva 對異步的處理是用 redux-saga 處理的,由於做者並不熟悉redux-saga,拿 redux-thunk 代替了。框架
第一步 建立store異步
const createStore = (reducer, initialState) => {
let currentReducer = reducer;
let currentState = initialState;
let listener = () => { };
return {
getState() {
return currentState;
},
dispatch(action) {
let {
type
} = action;
currentState = currentReducer(currentState, action);
listener();
return action;
},
subscribe(newListener) {
listener = newListener;
}
}
}
複製代碼
store 主要是 存儲數據,開放state更新接口。async
第二步 引入中間件 applyMiddleware函數
const compose = (...funcs) => {
if (funcs.length === 0) {
return arg => arg
}
if (funcs.length === 1) {
return funcs[0]
}
const last = funcs[funcs.length - 1]
const rest = funcs.slice(0, -1)
return (...args) => rest.reduceRight((composed, f) => f(composed), last(...args));
}
const applyMiddleware = (...middlewares) => {
return (createStore) => (reducer, initialState, enhancer) => {
var store = createStore(reducer, initialState, enhancer)
var dispatch = store.dispatch;
var chain = [];
var middlewareAPI = {
getState: store.getState,
dispatch: (action) => store.dispatch(action)
}
chain = middlewares.map(middleware => middleware(middlewareAPI))
dispatch = compose(...chain)(store.dispatch)
return {
...store,
dispatch
}
}
}
複製代碼
redux 中間件 洋蔥模型,修改了dispatch 方法。學習
引入異步中間件redux-thunk和logger中間件測試
const logger = store => next => action => {
console.log('prevState', store.getState());
let result = next(action);
console.log('nextState', store.getState());
return result;
};
const thunk = ({
dispatch,
getState
}) => next => action => {
if (typeof action === 'function') {
return action(dispatch, getState);
}
return next(action);
}
複製代碼
這裏引入 redux-thunk 作異步處理。
加入測試model
let counterModel = {
namespace: 'counter',
state: {
num: 0
},
reducers: {
add(state, action) {
console.log('reducer add executed');
return {
num: state.num + 1
}
},
asyncAdd(state, action) {
console.log('reducer asyncAdd executed');
return {
num: state.num + 1
}
},
test(state, action) {
console.log('reducer test executed');
return {
state
}
}
}
};
let userModel = {
namespace: 'user',
state: {
name: 'xxxx'
},
reducers: {
modify(state, {
payload
}) {
console.log('reducer modify executed');
let {
name
} = payload
return {
name
}
}
}
};
複製代碼
對不一樣model下的reducer進行分發
const combineReducer = (reducers) => (state = {}, action) => {
let {
type
} = action;
let stateKey = type.split('/')[0];
let reducer = type.split('/')[1];
reducers.map((current) => {
if (current.name === reducer) {
state[stateKey] = current(state[stateKey], action);
}
});
return state;
}
複製代碼
這裏由於 combineReducer 是 reducer的總入口,在這裏根據action 的 type 轉發到具體model下的reducer方法
dva 構造函數
class dva {
constructor() {
this._models = [];
this._reducers = [];
this._states = {};
}
model(model) {
this._models.push(model);
}
start() {
for (var i = 0; i < this._models.length; i++) {
this._states[this._models[i].namespace] = {
...this._models[i].state
};
Object.keys(this._models[i].reducers).map((key) => {
if (this._models[i].reducers.hasOwnProperty(key)) {
this._reducers.push(this._models[i].reducers[key]);
}
})
}
var rootReducer = combineReducer(this._reducers);
let createStoreWithMiddleware = applyMiddleware(thunk, logger)(createStore);
this._store = createStoreWithMiddleware(rootReducer, this._states);
this._store.subscribe(() => {
console.log(this._store.getState());
})
}
}
複製代碼
dva 構造方法主要工做是緩存model,建立store。
var app = new dva();
app.model(counterModel);
app.model(userModel);
app.start();
app._store.dispatch({
type: 'counter/add'
});
app._store.dispatch({
type: 'user/modify',
payload: {
name: 'shadow'
}
})
app._store.dispatch((dispatch, getState) => {
setTimeout(() => {
dispatch({
type: 'counter/asyncAdd'
})
}, 5000);
})
複製代碼
控制檯的輸出
這個固然是最粗糙的dva部分實現了,由於自己本身並無去看dva源碼,只是看了dva API 矇頭實現下,其中已經有不少很優秀的redux周邊生態,例如redux-thunk,logger等。固然也是複習了一下部分redux源碼了,看成本身學習的一個階段學習吧,最後像dva做者 陳謙 致敬。
最後留個地址吧:
http://oymaq4uai.bkt.clouddn.com/index.js