我爱Redux
作者:xiaoyu311
工作岗位:前端开发工程师
1 介绍Redux
- 1.1 Redux 是 JavaScript 状态容器,提供可预测化的状态管理(官方介绍)。其实他就是一个存储简单对象的容器(自我理解)。还是官方介绍逼格比较高。
- 1.2 Redux据说有三大原则
- 单一数据源(整个应用的 state 被储存在一棵 object tree 中,并且这个 object tree 只存在于唯一一个 store 中)。
- State 是只读的(唯一改变 state 的方法就是触发 action,action 是一个用于描述已发生事件的普通对象)
- 使用纯函数来执行修改(为了描述 action 如何改变 state tree ,你需要编写 reducers。reducers是一个纯函数)
看完介绍是不是懵逼了,你蒙我也蒙,让我们用代码说话,把懵逼统统赶跑。
2 敲起我们的代码来
2.1 新建目录结构
- redux
- src
- isPlainObject.js
- applyMiddleware.js
- bindActionCreators.js
- combineReducers.js
- compose.js
- createStore.js
- src
2.2 isPlainObject.js(判断是否为简单对象)
function isPlainObject(obj) {
if (typeof obj !== 'object' || obj === null) return false;
let proto = obj; // 拷贝对象
while(Object.getPrototypeof(obj) !== null) {
proto = Object.getPrototypeof(obj); // 循环深度遍历obj,直到proto指向Object.prototype
}
return Object.getPrototypeof(obj) === proto;// 如果为true的话,说明对象是通过字面量或者new Object()创建的对象
}
2.3 createStore.js(仓库创建)
// redux依赖于发布订阅模式listeners必然是少不了的
function createStore(reducer, preloadedState, enhancer) {
// 把函数缓存到数组中 等待被调用
let currentListeners = [];
let currentState = preloadedState;
let initailState;
function getState(){
return currentState;
}
// 注册函数
function subscribe(listener) {
if (typeof listener !== 'function') {
throw new Error('压根不是一个函数')
}
currentListeners.push(listener);
// 返回一个 取消订阅函数
return function unsubscribe(){
currentListeners.filter(l => l !== listener);
}
}
// 配发一个动作
function dispatch(action){
if (!isPlainObject(action)) {
throw new Error(`${action} 压根不是一个简单对象`);
}
if (typeof action.type === undefined) {
throw new Error(`type 应该是一个字符串啊`);
}
// 第一次reducder执行 initailState为 undefined 所以reducer函数需要给一个初始状态state
initailState = reducer(initailState, action);
currentListeners.forEach(fn => fn()); // 执行所有的 订阅 函数
return action;
}
dispatch({type: '@@INIT'}); // 首次执行 一次给 initailState赋值(也就是你自己定义的state)
return {
dispatch,
subscribe,
getState
}
}
2.4 combineReducers.js(合并reducer 进而合并state)
// combinReducers 是为了 合并reducers 此函数 显然还是要返回一个reducer
function combinReducers(reducers){
// 这是一个reduer函数哟!
return (state = {}, action) => {
// 拿到你定义所有reducer 对应的key
const finalReducerKeys = Object.keys(reducers);
// 遍历所有reducer
finalReducerKeys.forEach(key => {
state[key] = reducers[key](state[key], action)
// reducers[key] 拿到对应的 reducer执行
// 首次执行 state[key]为undefined,可是你别忘了 你自己reducer传入了初始状态 state呦!
// state[key] 重新 给每个reducer对应的state赋值
});
return state;
}
}
2.5 bindActionCreators.js(创建action,解决自己手动dispatch问题)
function bindActionCreators (actions, dispatch) {
const memo = {};
Object.keys(actions).reduc((m, key) => {
// 获取当前 创建 action的函数
const actionCreator = actions[key];
// actionCreator函数名字 对应一个 能直接派发动作的 函数
memo[key] = (...args) => dispatch(actionCreator(...args));
}, memo);
return memo;
}
2.6 重点来了 applyMiddleware.js 这才是王道!!!
介绍
applyMiddleware之前 我们先来看一个简单的中间件函数
// 三层函数
// 第一层函数 接收一个对象作为参数 返回了一个 新函数
// 第二层函数 接收一个next 参数继续返回一个新函数
// 第三层函数 接收一个action 作为参数,还记得 dispatch函数吗,是不是也接收一个参数而且也是action,好巧啊
const logger = ({ dispatch, getState }) => next => action => {
console.log(getState()); // 打印 之前状态
next(action);
console.log(getState()); // 打印 之后的状态
}
中间件函数会传到
applyMiddleware方法中调用, 给中间件 传递 需要到参数
// 调用 applyMiddleware 方式为`applyMiddleware(middlewares)(createStore)(reducer)`
function applyMiddleware(...middlewares){
return createStore => (...args) => {
// 调用createStore生成 store
const store = createStore(...args);
let dispatch;
// 重写dispatch方法
const middlewareApi = {
getState: store.getState,
dispatch: (...args) => dispatch(...args)
};
// 把所有中间件 执行后 放到一个数组中 chain 成了[fn1, fn2, fn3];
const chain = middlewares.map(middleware => middleware(middlewareApi));
// fn3函数调用返回值 传给fn2,fn2函数调用返回值 传给fn1,fn1继续调用
dispatch = compose(...chain)(store.dispatch);
return {
...store,
dispatch
}
}
}
2.7 compose.js(中间件合并)
function compose (...funs) {
let i = funs.length;
return function(...args) {
let result = args;
// 从右向做开始调用数组中的函数 每一个函数到返回值,作为下一个函数调用时候到参数
while(i>=0) {
result = funs[i--](...result);
}
// 也就是说 fn3函数调用 返回的就是 middlewareApi
return result;
}
}
然而官方并不是这么写的(下面就是官方实现)(逼格还是相当高的)
function compose(...funs) {
if (funcs.length === 0) {
return arg => arg
}
if (funcs.length === 1) {
return funcs[0]
}
return funs.reduc((a, b) => (...args) => a(b(...args)));
}
Redux还有一些不常用的方法,就不再去实现了,自我感觉,中间件这块,还是有点绕。毕竟大牛实现。让我们一起学习Redux源码,都成为大牛 🐂🐂🐂