解析:让你弄懂redux原理
作者: HerryLo
本文永久有效链接: https://github.com/AttemptWeb......
Redux是JavaScript状态容器,提供可预测化的状态管理。
在实际开发中,常搭配React + React-redux使用。这代表了目前前端开发的一个基本理念,数据和视图的分离。
redux应运而生,当然还有其他的一些状态管理库,如Flux、Elm等,当然,我们这里只对redux进行解析。
#redux创建Store
创建redux的store对象,需要调用combineReducers和createStore函数,下面解释不包含中间件。
const reducer = combineReducers({
home: homeNumber,
number: addNumber
})
const store = createStore(reducer)
// 暂时挂载在window下,下面会使用到
window.$reduxStore = store
#combineReducers函数
首先调用combineReducers函数,将多个reducer函数作为参数传入,源码如下:
// reducers即是传入的参数对象
function combineReducers(reducers) {
// ......省略
return function combination(state = {}, action) {
let hasChanged = false
const nextState = {}
for (let i = 0; i < finalReducerKeys.length; i++) {
// finalReducerKeys 是传入reducers对象的key值
const key = finalReducerKeys[i]
// finalReducers 等价于 reducers
const reducer = finalReducers[key]
const previousStateForKey = state[key]
// 运行reducer函数,返回一个state
// 核心:调用combination函数,实际就是循环调用传入的reducer函数
const nextStateForKey = reducer(previousStateForKey, action)
nextState[key] = nextStateForKey
// hasChanged = hasChanged || nextStateForKey !== previousStateForKey
}
// hasChanged = hasChanged || finalReducerKeys.length !== Object.keys(state).length
// 返回state对象
return nextState
}
}
// 源码地址:https://github.com/reduxjs/redux/blob/master/src/combineReducers.ts#L139
上面的代码其实非常简单,combineReducers函数运行,返回一个新的combination函数。combination函数的主要作用是返回一个挂载全部state的对象。 当combination函数被调用时,实际就是循环调用传入的reducer函数,返回state对象。将combination函数作为参数传入到createStore函数中。
#createStore函数
function createStore(reducer, preloadedState, enhancer) {
// reducer --> combination函数
let currentReducer = reducer
// 全部的state属性,挂载在currentState上
let currentState = preloadedState
// 下面的中间件会用到
if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
// 第二个参数是一个函数,没有第三个参数的情况
enhancer = preloadedState
// 将preloadedState重置
preloadedState = undefined
}
if (typeof enhancer !== 'undefined') {
// 存在中间件时,将createStore传入中间件函数,调用enhancer函数,return结束。
return enhancer(createStore)(reducer, preloadedState)
}
function dispatch(action) {
// currentReducer --> combination函数
currentState = currentReducer(currentState, action)
}
// 初始化调用dispatch,创建初始state
dispatch({ type: ActionTypes.INIT })
const store = ({
dispatch: dispatch,
subscribe,s
getState,
replaceReducer,
[$$observable]: observable
}
return store
}
// 源码地址:https://github.com/reduxjs/redux/blob/master/src/createStore.ts#L60
reducer
就是传入的combination函数,preloadedState是初始化的state(没有太大的作用),enhancer是中间件,没有第三个参数enhancer的情况下,同时第二个参数preloadedState是一个函数,preloadedState被赋值给enhancer。
调用dispatch
函数初始化,currentReducer
即是传入combination
函数,就向上文提到的,调用combination
函数实际就是循环调用reducer函数。所有的state对象,被挂载在内部变量currentState
上。存在中间件enhancer时,将createStore传入中间件函数,调用enhancer函数,return结束,这个下文会继续讲到。
创建的store对象,暴露出的方法如下:
const store = ({
// 分发 action,这是触发 state 变化的惟一途径。
dispatch: dispatch as Dispatch<A>,
// 变化监听器
subscribe,
// 获取store下的 全部state
getState,
// 替换 store 当前用来计算 state 的 reducer
replaceReducer
}
return store
dispatch
函数触发action,调用reducer函数,修改state。subscribe
函数可以监听变化state的变化。getState
函数获取全部state。replaceReducer
函数替换用来计算state的reducer
函数。
通过combineReducers
函数合并reducer函数,返回一个新的函数combination
(这个函数负责循环遍历运行reducer函数,返回全部state)。将这个新函数作为参数传入createStore
函数,函数内部通过dispatch,初始化运行传入的combination
,state生成,返回store对象
#redux中间件
最好把上面看懂之后,再看中间件部分!!下面对中间件进行分析:
redux-thunk
只是redux中间件的一种,也是比较常见的中间件。redux-thunk
库允许你编写与store交互的异步逻辑。
import thunkMiddleware from 'redux-thunk'
const reducer = combineReducers({
home: homeNumber,
number: addNumber
})
const store = createStore(
reducer,
applyMiddleware(
thunkMiddleware, // 异步支持
)
)
createStore函数支持三个参数,如果第二个参数preloadedState是一个函数,而没有第三个参数enhancer的话,preloadedState会被赋值给enhancer。
下面会以redux-thunk
中间件作为例子,下面就是thunkMiddleware函数的代码:
// 部分转为ES5代码,运行middleware函数会返回一个新的函数,如下:
return ({ dispatch, getState }) => {
// next实际就是传入的dispatch
return function (next) {
return function (action) {
// redux-thunk核心
if (typeof action === 'function') {
return action(dispatch, getState, extraArgument);
}
return next(action);
};
};
}
// 源码地址:https://github.com/reduxjs/redux-thunk/blob/master/src/index.js
redux-thunk
库内部源码非常的简单,github: redux-thunk 源码,允许action是一个函数,同时支持参数传递,否则调用方法不变。
#applyMiddleware函数
// 中间件调用
return enhancer(createStore)(reducer, preloadedState)
等价于
return applyMiddleware(
thunkMiddleware,
)(createStore)(reducer, preloadedState)
redux的中间件,从applyMiddleware函数开始,它主要的目的就是为了处理store的dispatch函数
。
// 支持多个中间件传入
export default function applyMiddleware(...middlewares) {
return (createStore) => (reducer, ...args) => {
// 创建 store
const store = createStore(reducer, ...args)
const middlewareAPI = {
getState: store.getState,
dispatch: (action, ...args) => dispatch(action, ...args)
}
// 遍历运行中间件函数,将middlewareAPI作为参数传入
// middleware对应上面的redux-thunk库核心代码,当然也支持多个中间件
const chain = middlewares.map(middleware => middleware(middlewareAPI))
// 核心:将所有中间件传入到compose中,返回一个新的dispatch
dispatch = compose(...chain)(store.dispatch)
// 照常返回一个store对象,dispatch已经被处理过了
return {
...store,
dispatch
}
}
}
// 源码地址:https://github.com/reduxjs/redux/blob/master/src/applyMiddleware.ts#L55
applyMiddleware
函数接收多个middlewares参数,返回一个store对象。通过createStore创建store对象,middlewareAPI对象挂载getState和dispatch,循环middlewares中间件,将middlewareAPI作为参数传入每个中间件。遍历结束以后,拿到了一个包含所有中间件新返回函数的一个数组,将其赋值给变量chain。
// 遍历之后chain的值,这里只是拿redux-thunk库作为例子
// next 就是 dispatch
chain = [function (next) {
return function (action) {
if (typeof action === 'function') { // redux-thunk核心
return action(dispatch, getState, extraArgument);
}
return next(action);
};
}, ...更多中间件]
#compose函数
// 数组chain 保存所有中间件新返回函数
dispatch = compose(...chain)(store.dispatch)
compose的主要作用就是运行所有中间件函数后,返回一个经过处理的dispatch函数。
// compose函数
return chain.reduce((a, b) =>{
return (...args)=> {
return a(b(...args))
}
}
chain是保存中间件函数的数组,具体的内部结构参见上面
解析:让你弄懂redux原理的更多相关文章
- 彻底弄懂jsonp原理及实现方法
一. 同源策略 所有支持Javascript的浏览器都会使用同源策略这个安全策略.看看百度的解释: 同源策略,它是由Netscape提出的一个著名的安全策略. 现在所有支持JavaScript 的浏览 ...
- 这一次,彻底弄懂 Promise 原理
作者声明 本人将迁移至个人公众号「前端Q」及「掘金」平台写文章.博客园的文章将不再及时更新发布.欢迎大家关注公众号「前端Q」及我的掘金主页:https://juejin.im/user/5874526 ...
- 彻底弄懂HTTP缓存机制及原理-转载
首先附上原文地址,非常感谢博主大神的分享彻底弄懂HTTP缓存机制及原理 前言 Http 缓存机制作为 web 性能优化的重要手段,对于从事 Web 开发的同学们来说,应该是知识体系库中的一个基 ...
- Java进阶专题(二十六) 将近2万字的Dubbo原理解析,彻底搞懂dubbo
前言 前面我们研究了RPC的原理,市面上有很多基于RPC思想实现的框架,比如有Dubbo.今天就从Dubbo的SPI机制.服务注册与发现源码及网络通信过程去深入剖析下Dubbo. Dubbo架构 ...
- 为了弄懂Flutter的状态管理, 我用10种方法改造了counter app
为了弄懂Flutter的状态管理, 我用10种方法改造了counter app 本文通过改造flutter的counter app, 展示不同的状态管理方法的用法. 可以直接去demo地址看代码: h ...
- 彻底弄懂LSH之simHash算法
马克·吐温曾经说过,所谓经典小说,就是指很多人希望读过,但很少人真正花时间去读的小说.这种说法同样适用于“经典”的计算机书籍. 最近一直在看LSH,不过由于matlab基础比较差,一直没搞懂.最近看的 ...
- 彻底弄懂AngularJS中的transclusion
点击查看AngularJS系列目录 彻底弄懂AngularJS中的transclusion AngularJS中指令的重要性是不言而喻的,指令让我们可以创建自己的HTML标记,它将自定义元素变成了一个 ...
- Golang, 以 9 个简短代码片段,弄懂 defer 的使用特点
作者:林冠宏 / 指尖下的幽灵 掘金:https://juejin.im/user/587f0dfe128fe100570ce2d8 博客:http://www.cnblogs.com/linguan ...
- [转]js模块化编程之彻底弄懂CommonJS和AMD/CMD!
原文: https://www.cnblogs.com/chenguangliang/p/5856701.html ------------------------------------------ ...
随机推荐
- mysql abs() 获取绝对值
mysql> -); +----------+ | abs(-) | +----------+ | | +----------+ row in set (0.00 sec)
- 关于js中onload事件的部分报错。
当使用onload获取元素时,建议在onload事件之前定义需要获取的元素名称,在onload里面只执行获取操作,这样获取到的元素在后面才能顺利使用. <!DOCTYPE html> &l ...
- 作业——12 hadoop大作业
作业的要求来自于:https://edu.cnblogs.com/campus/gzcc/GZCC-16SE2/homework/3339 Hadoop综合大作业 1.以下是爬虫大作业产生的csv文件 ...
- 3ds Max学习日记(十二)——用Maxscript将每一帧动画导出成obj
参考链接: is there a way to through maxscript to make the time slider go to a spacific frame? 最近老师布置了要用m ...
- 2019软工实践_Alpha(6/6)
队名:955 组长博客:https://www.cnblogs.com/cclong/p/11913269.html 作业博客:https://edu.cnblogs.com/campus/fzu/S ...
- Spark2.x(五十五):在spark structured streaming下sink file(parquet,csv等),正常运行一段时间后:清理掉checkpoint,重新启动app,无法sink记录(file)到hdfs。
场景: 在spark structured streaming读取kafka上的topic,然后将统计结果写入到hdfs,hdfs保存目录按照month,day,hour进行分区: 1)程序放到spa ...
- Mac复制粘贴文本时默认使用无格式模式
参考:How to Paste Everything as Plain Text 写文章的时候,用的最多的就是copy和paste了,可是现在Mac和Win默认都是会连格式一起复制,真是逆天,导致每次 ...
- SQL优化:一些简单的又实用的SQL优化方案【转】
面试过程中,面试官有极高的频率会问道数据库的优化,SQL语句的优化,网上关于SQL优化的教程很多,但是鱼目混杂,显得有些杂乱不堪.近日有空整理了一下,写出来跟大家分享一下,其中有错误和不足的地方,还请 ...
- zipkin exec下载地址
zipkin exec下载地址 https://repo1.maven.org/maven2/io/zipkin/java/zipkin-server/1.20.1/zipkin-server-1.2 ...
- Laya的滚动容器
想实现一个简单的滚动容器.例如水平排列10个图标,可以左右滑动查看的. Egret里有布局容器可以滚动 Laya看了教程和示例,没有找到一个滚动容器,只有一个list,需要设置item,显然不是我想要 ...