提起 redux小伙伴们应该都不陌生,著名的第三方状态管理库,也是很多小伙伴进阶路上必然攻克的源码之一。Redux 除了和 React 一起用外,还支持其它界面库。 它体小精悍(只有2kB,包括依赖)。
今天我们就来说说redux学习中需要重点学习的东西;
三大原则
Redux 可以用这三个基本原则来描述:
单一数据源
整个应用的 state 被储存在一棵 object tree 中,并且这个 object tree 只存在于唯一一个 store 中。
State 是只读的
唯一改变 state 的方法就是触发 action,action 是一个用于描述已发生事件的普通对象。
使用纯函数来执行修改
为了描述 action 如何改变 state tree ,你需要编写 reducers。
使用
其实redux使用起来大致可以分为一下几步骤:
声明store文件(存放state)
// store.js
import { createStore } from 'redux';
import reducer from "./reducer.js";
const store = createStore(reducer);
export default store;
声明reducer.js文件(修改state)
声明的reducer文件主要是为了修改保存的state
export default const counter = (state = 0, action) => {
switch (action.type) {
case 'INCREMENT':
return state + 1;
case 'DECREMENT':
return state - 1;
default:
return state;
}
};
dispatch触发action动作、subscribe订阅以及unsubscribe取消订阅
store.dispatch({ type: 'INCREMENT' });
const unsubscribe = store.subscribe(()=> {});
知识点
以上是我们需要了解的基础,其实redux摊开了说白了也没有什么神秘的就是一个第三方文件保存state,用特定action去触发修改,我们需要了解的真正核心是redux对于createStore中参数enhancer
的处理以及applyMiddleware
的实现,这才是redux的核心;
enhancer
enhancer翻译过来就是增强器,其增强的对象就是dispatch
, 允许我们对dispach进行更改,在源码中我们可以看到:
87行的判断如果enhancer存在并且是一个函数的情况下,createStore会在96行提前返回一个enhancer执行的柯里话函数;
由于enhancer需要对dispatch进行增强,所以enhancer需要createStore作为参数,并且在增强过程中需要调用dispatch所以需要用到reduer、preloadedState,至于柯里化更多的是为了方便组合吧!
enhancer这个函数的结果其实就是applyMiddleware
函数聚合中间件的以后返回的新的store
以及dispatch
;
源码:在这里
applyMiddleware
接下来我们就一起来看下middleware函数都做了什么,他是怎么聚合中间件并返回新的store以及它又是如何增强dispatch的吧!
源码是用ts编写的对于一些人来说看起来比较麻烦,我这里改成js的写法,并且去掉了多余的throw。
以下就是applyMiddleware
函数的大致框架,首先我们需要在enhancer
传进来的参数中取出原始store以及dispatch,并且经过一系列的操作增强出新的dispatch,并返回新的store以及dispatch
export default const applyMiddleware = (...middleware) => {
return createStore => (reducer, preloadedState) => {
let store = createStore(reducer, preloadedState)
let dispach = store.dispach
// todo 增强dispatch
return {...store, dispach}
}
}
我们增强dipatch需要执行所有的中间件函数, 因为中间件函数最后也要处理dispatch,访问状态仓库的状态值,这也就意味着我们需要给中间件提供redux的控制权。
比如我们使用redux-logger打印的时候我们打印的就是状态库中的状态值,如果拿不到redux的控制权那就访问不到store的状态值
其实所谓的控制权就是getStore 以及dispatch;
所以我们这样来做声明一个middlewareAPI用来接收状态仓库的控制权,我们只需要以参数的形式传给中间件函数就可以了,大致如下;
+为新增逻辑
export default const applyMiddleware = (...middleware) => {
return createStore => (reducer, preloadedState) => {
let store = createStore(reducer, preloadedState)
let dispach = store.dispach
// todo 增强dispatch
// 声明状态仓库的控制圈对象
+ const middlewareAPI = {
+ getState: store.getState,
+ dispatch: (action, ...args) => dispatch(action, ...args)
+ }
// 聚合中间件,中间是多个我们遍历执行每一个中间件,并将控制权对象作为参数传进去。
+ const chain = middlewares.map(middleware => middleware(middlewareAPI));
// 返回的chain是一个新的数组,由每一个拿到控制权的中间件函数组成
// 源码中用了一个compose文件专门来处理中间件的聚合
// 为了减轻用户的负担,不可能每一个中间件都要用户手动执行,所以这里选择用compose聚合执行一下,让用户直接拿到最后的结果。
+ dispatch = compose(...chain)(store.dispatch)
return {...store, dispach}
}
}
细心的小小伙伴应该注意到了一个问题也就是我们的仓库控制权对象中的getState是直接在store中取出来的,而dispatch却有再次封装了一下,这是什么原因呢?
这里讲解下,我们使用的dispatch,是希望使用加强后的dispatch,因为他可以处理一些问题,如果我们想getStore一样在store中获取,那我传下去的依旧是老的dipatch,那增强不就没有意义了吗,所以我们这里希望使用的是上下文中的dispatch,也就是增强后的,所以这里要封装一次。
源码请移步这里
compose
export default function compose(...funcs) {
if (funcs.length === 0) {
// 这里是没有中间件的时候,但是又有参数的情况,直接声明一个函数返回参数
return arg => arg
}
// 这里是只有一个中间件的时候避免使用reduce,直接返回唯一的一个中间件函数
if (funcs.length === 1) {
return funcs[0]
}
return funcs.reduce(
(a, b) =>
(...args) =>
a(b(...args))
)
}
源码在这里
compose 的核代码确实只有那么点,作用在在于将所有中间件以嵌套的形式执行一遍,并返回以后最终的结果。类似于fn1(fn2(fn3(params)))
不懂的可以移步我的另一篇博客,[[函数合成(compose)的多种实现方法]!](https://juejin.cn/post/700211...)
至于combineReducers 以及bindActionCreators 就不赘述了,自己看也能看的懂,就是个简单的合并store的状态。