大纲
1、store、state、action、store.dispatch、reducer的概念
2、redux 单向数据流流程说明
3、实现一个 redux
4、中间件日志打印及异步调用实现
代码地址: https://github.com/huangwenjuning/offer-coming-soon/tree/master/redux
基础概念
- store
整个项目只能有一个 store,store 可以看做是一个数据容器, 用于保存数据。
redux 提供了 createStore 方法用于生成 store。 - state
state 表示的是当前时刻的数据。
redux 提供了 getState 方法用于获取当前时刻的数据集合。 - action
action 表示的是当前要发生改变。即 view 层发出通知,表示 state 将要以某种规则发生变化。
action 是一个对象,它具有 type 和 payload 的属性:
const action = {
type: 'ADD_TODO',
payload: 'Learn'
}
- store.dispatch
store.dispatch 是 view 分发 action 的唯一方法,它接收一个 action。
store.dispatch({
type: 'ADD_TODO',
palyload: 'Learn'
})
-
reducer
store 接收到 action 后,必须返回一个新的 state, 这样 view 才会发生更新,这种 state 的计算过程就是 reducer。Reducer 是一个纯函数,它接收旧的 state 和 action 为参数,并返回一个新的 state。
只要传入的参数相同,那么得到结果也必定相同。因此保持 reducer 纯净非常重要。
我们不应该在 reducer 中做这些具有副作用的操作:
(1)修改传⼊参数;
(2)执⾏有副作⽤的操作,如 API 请求;
(3)调⽤非纯函数,如 Date.now() 或 Math.random()。
redux 工作流程

我们简述一下 redux 完成一次数据更新的过程:
A. 首先,用户通过操作发出 Action ——> store.dispatch
B. store 会自动调用 reducer 方法,并传入当前 action 与 state,reducer 经过预定数据处理之后,会返回一个新的 state。 ——> reducer(action, state)
C. state 发生变化,store 调用监听函数。 ——> store.subscribe()
实现redux
redux 提供了以下几个 api 我们先了解一下:
-
createStore: 用于创建一个 store
const store = createStore(reducer); -
reducer: 用于数据的初始化以及变更数据状态的函数
const reducer = (state = 0, action) => { switch(action.type) { case 'ADD': return state+1; case 'MINUS': return state-1; case 'AsyncAdd': return state+1; default: return 0; } }; -
getState(): 用于获取当前时刻的 state 数据
-
dispatch(): 用于分发 action
store.dispatch({type: 'ADD'}); -
subscribe(): 用于数据订阅,数据更新时调用执行
createStore.js
我们创建一个 createStore 方法,该方法会返回 getState、dispatch 和 subscibe() 方法。
const createStore = (reducer, enhancer) => {
// 当前数据
let state;
// 进行依赖收集(订阅者)
let listeners = [];
if (enhancer) {
// 中间件,对 dispatch 的增强过程
return enhancer(createStore)(reducer);
}
// 获取当前的数据
const getState = () => {
return state;
};
// 进行订阅和退订
const subscribe = (listener) => {
listeners.push(listener);
return () => {
listeners.filter((item) => item !== listener);
}
};
// 分发 action
const dispatch = (action) => {
state = reducer(state, action);
// state 更新,通知订阅者
listeners.forEach((listener) => listener());
return action;
};
// 手动执行一次,用于初始化
dispatch({});
return {
getState,
subscribe,
dispatch,
}
}
applyMiddleware.js
const applyMiddleware = (...middlewares) => {
return createStore => reducer => {
const store = createStore(reducer);
let dispatch = store.dispatch;
const midApi = {
getState: store.getState,
dispatch: (...arg) => dispatch(...arg),
}
// 每一个中间件都接收 getState 和 dispatch 方法
const middlewareChain = middlewares.map((mid) => mid(midApi));
// 将中间件组合成一个函数进行调用
dispatch = compose(...middlewareChain)(dispatch);
return {
...store,
dispatch,
}
}
};
var compose = (...mid) => {
if (mid.length === 0) {
// 如果没有传入中间件,直接返回参数
return arg => arg;
}
if (mid.length === 1) {
// 如果只有一个中间件,则直接返回
return mid[0]
}
// 将中间件聚合成 f1(f2(f3(args)) 形式并返回
return mid.reduce((l, r) => (...args) => l(r(...args)));
}
combineReducers.js
const combineReducers = (reducers) => {
return (state = {}, action) => {
const reducerKeys = Object.keys(reducers);
// 新的 state
const newState = {};
// 判断 state 是否发生变化
let hasChanged = false;
for (let i = 0; i < reducerKeys.length; i++) {
const key = reducerKeys[i];
const reducer = reducers[key];
// 获取当前 key 旧的 state
const preStateForKey = state[key];
// 得到当前 key 新的 state
const nextStateForKey = reducer(preStateForKey, action);
if (typeof nextStateForKey !== 'undefined') {
newState[key] = nextStateForKey;
hasChanged = hasChanged || nextStateForKey !== preStateForKey;
}
}
hasChanged = hasChanged || Object.keys(reducers).length !== Object.keys(state).length;
return hasChanged ? newState : state;
}
};
中间件日志记录和异步调用实现
thunk.js
进行异步调用时,我们这样分发 action:
store.dispatch((dispatch) => {
setTimeout(() => {
dispatch({type: 'AsyncAdd'});
}, 3000);
});
const thunk = ({ getState, dispatch }) =>
(next) => (action) => {
if (typeof action === 'function') {
return action(dispatch, getState);
}
return next(action);
}
logger.js
const logger = ({ getState }) => {
return (next) => (action) => {
const state = getState();
console.log('=====');
console.log('preState', state);
console.log('action', action);
next(action);
const curState = getState();
console.log('curState', curState);
console.log('=====');
}
}
参考
写在后面:首发于个人博客http://agangxuezhang.cn/single-minded

1288

被折叠的 条评论
为什么被折叠?



