开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 9 天,点击查看活动详情
前言
手写redux系列(小白级教程,简单易懂)(一) - 掘金 (juejin.cn) 自从上次写了《手写redux系列》以后,好久没更新了,今天我们继续来实现redux中的applyMiddleware功能,来让他看上去更完善,本次的代码也是继续在上次的代码上进行编写,如果有小伙伴还没有看过redux系列文章一,也可以先看了后,把里面的代码实现之后,再来追更这篇文章
what is middleware?
和大多数框架一样,redux也不是完善的,所以他开了一个口子,可以供社区去开发一些中间件,来完善redux的功能。
例如我们常见的middleware: redux-thunk:用于异步操作、redux-logger:用于日志记录
我的理解是:middleware本身是一个函数,middleware是用来增强dispatch的,在dispatch分发action之前和之后增加一些功能,本身是一个装饰器模式
why do we learn applyMiddleware?
middleware中间件,学会中间件,你就学会了代理模式applyMiddleware本身采用了装饰器模式,你又能学会装饰器- 如何把众多
middleware合并到一起,你又可以学习compose函数
这笔买卖发财了呀
理解enhancer函数概念
enhancer 的中文意思是增强器,是一个名词
那么在哪里会用到呢??
我们在上一次的文章中,实现了createStore函数,当时只接受了一个参数,reducer
function createStore(reducer){}
为了实现中间件功能,我们要增加enhancer作为第二个参数,也就是说第二个参数传入一个enhancer
function createStore(reducer,enhancer){}
enhancer的特点是什么?
enhancer是一个装饰器模式,装饰器模式的特点是什么,在不改变原有的结构和功能下,添加一些新的功能
装饰器模式一般怎么设计?
传入什么返回什么
redux的核心是什么?
createStore
所以我们把createStore传给enhancer,返回一个新的createStore
因为此处的enhancer主要是用来增强createStore的,所以我们也可以称为enhanceCreateStore
而增强功能的步骤在enhancer内部实现
现在我们在createStore内部先处理一下enhancer
function createStore(reducer, enhancer) {
// 先处理enhancer
// 如果enhancer存在并且是函数
if (enhancer && typeof enhancer === 'function') {
// 我们将createStore作为参数传给他
// 传入什么返回什么,所以他会返回一个新的createStore给我们,所以名字叫newCreateStore
const newCreateStore = enhancer(createStore);
// 新的newCreateStore和原有的createStore函数一致,都是接受reducer
// 得到的也一致,是一个store,但这是一个增强了功能的store,所以我们叫他newStore
const newStore = newCreateStore(reducer);
// 最后直接返回就行了
return newStore;
}
// 闭包存储状态
let state;
// 初始化订阅者数组
const listeners = [];
...
}
实现applyMiddleware
我们先在applyMiddleware中实现enhancer函数
function applyMiddleware(middleware) {
// applyMiddleware 的返回值应该是一个enhancer
// 按照我们前面说的enhancer的参数是createStore
function enhancer(createStore) {
// enhancer应该返回一个新的createStore
function newCreateStore(reducer) {
// 我们先写个空的newCreateStore, 直接返回createStore的结果
const store = createStore(reducer);
return store;
}
return newCreateStore;
}
return enhancer;
}
写好了上面的步骤以后,我们来实现中间件函数
中间件特点
-
中间件也是返回一个增强器,是用来增强
dispatch的,所以此处的middleware函数的返回值也可以叫做enhanceDispatch -
而且
middleware的返回值enhanceDispatch也采用了装饰器模式,传入什么返回什么
在enhancer中处理中间件函数
function applyMiddleware(middleware) {
// applyMiddleware 的返回值应该是一个enhancer
// 按照我们前面说的enhancer的参数是createStore
function enhancer(createStore) {
// enhancer应该返回一个新的createStore
function newCreateStore(reducer) {
// 我们先写个空的newCreateStore, 直接返回createStore的结果
const store = createStore(reducer);
// middleware是一个函数,返回enhancerDispatch增强器
const enhanceDispatch = middleware(store);
// 获取老的dispatch
const { dispatch } = store;
// enhancerDispatch,是用来增强dispatch的,所以传入dispatch,返回一个新的dispatch
const newDispatch = enhanceDispatch(dispatch)
// 用新的dispatch,替换老的dispatch
return { ...store, dispatch: newDispatch };
}
return newCreateStore;
}
return enhancer;
}
接下来我们手写一个logger中间件
function logger(store) {
// 传入dispatch返回一个新的dispatch
function enhancerDispatch(dispatch) {
// 新的dispatch和老的dispatch一样,接受action作为参数
function newDispatch(action) {
// 分发action前可以做一些处理
let result = dispatch(action);
// 分发action后可以做一些处理
return result
}
return newDispatch;
}
// 返回enhanceDispatch增强器
return enhanceDispatch;
}
接下来添加一些打印日志功能
function logger(store) {
return function(next) {
return function(action) {
console.group(action.type);
console.info('dispatching', action);
let result = next(action);
console.log('next state', store.getState());
console.groupEnd();
return result
}
}
}
这样子我们的中间件功能就大功告成了
合并多个中间件
中间件都是什么类型?
都是函数类型,而且接受同样的参数
所以就是合并多个函数,并且执行,返回最终结果
让我们想到了compose函数,用来合并并执行函数的
compose函数实现
function compose(func) {
// 如果只有一个函数的话
if (func.length === 1) {
// 返回第一个函数
return func[0];
}
return func.reduce((a, b) => {
// 此处返回什么,就证明compose是什么
// 先执行a函数,在把a的结果传给b,在执行b
return (...args) => b(a(...args));
})
}
所以我们再改一下applyMiddleware函数
function applyMiddleware(...middlewares) {
// applyMiddleware 的返回值应该是一个enhancer
// 按照我们前面说的enhancer的参数是createStore
function enhancer(createStore) {
// enhancer应该返回一个新的createStore
function newCreateStore(reducer) {
// 我们先写个空的newCreateStore, 直接返回createStore的结果
const store = createStore(reducer);
// 因为我们只要是传入dispatch的,所以这边先展开middleware
const chain = middlewares.map((middleware) => middleware(store));
// 获取老的dispatch
const { dispatch } = store;
// 使用compose合并并执行中间件
const enhanceDispatch = compose(chain);
const newDispatch = enhanceDispatch(dispatch);
// 用新的dispatch,替换老的dispatch
return { ...store, dispatch: newDispatch };
}
return newCreateStore;
}
return enhancer;
}
接下来我们再写一个中间件用来测试
function logger2(store) {
return function(next) {
return function(action) {
let result = next(action);
console.log('logger2');
return result
}
}
}
最后点击一下我是周杰伦,我在油管新发布了一首歌,在控制台打印的信息包括了两个中间件的日志,所以我们的实现是没有问题的
完美,撒花 ✿✿ヽ(°▽°)ノ✿