Redux 源码解析
闲谈
好些个日子没有写博客了,脑子里头想着有好些事情该干的该写的,但平日里就又陷入实验室与课程所带来无穷的琐事中了。偶尔偷得闲,还是写两篇罢,老是囤在笔记里头日晒不着的,眼瞅着就要发霉生虫了。
Redux
Redux源码的设计简介明白,拢共加起来也不到千行,注释写的也清清楚楚,只要心里头对于Redux的设计理念有个大致的了解,源码就能很轻松的阅读。
Redux 是 JavaScript 状态容器,提供可预测化的状态管理。
Redux 由 Flux 演变而来,但受 Elm 的启发,避开了 Flux 的复杂性。
官网的这两句介绍就把Redux的来龙去脉介绍的一清二楚了,要理解Redux的设计思路,首先得对Redux的核心概念和函数式编程思想有一定的认知。
这儿就不对这些预备知识进行介绍了,Redux文档中已经给出了非常细致的介绍(其实Redux文档中涵盖了方方面面的介绍,例如核心思想、使用技巧、API文档等,这些内容比单单分析Redux源码更有价值)。函数式编程在Redux源码中大概体现在Currying、Compose以及纯函数,只需花10分钟简单了解即可。
Redux 源码结构
首先来看一眼Redux源码的文件结构:
文件结构很清爽,包含了6个核心文件和3个工具文件,简单列个表来看看其大致功能:
文件 | 功能 | 备注 |
---|---|---|
index.js |
入口文件 | |
createStore.js |
提供 createStore API | |
compose.js |
函数组合 | 虽然也是工具文件的功能但是不知道为什么不放在 utils 目录下 |
combineReducers.js |
提供了 combineReducers API,同时包含了一些 reducer 的验证函数 | |
bindActionCreators.js |
提供了 bindActionCreators API | |
applyMiddleware.js |
提供了 applyMiddleware API | |
warning.js |
显示 warning 提示信息 | 工具文件 |
isPlainObject.js |
判断对象是否是一个普通对象 | 工具文件 |
actionTypes.js |
提供预设的 action | 工具文件 |
工具函数
工程中所使用的工具文件包含在 utils
文件夹下(compose.js
也算在里头),简单介绍其中的一部分:
// compose.js
// Redux 中 compose 函数和函数式编程中是同个概念,就是对函数进行组合
// compose(f, g) --> f(g)
export default function compose(...funcs) {
if (funcs.length === 0) {
return arg => arg
}
if (funcs.length === 1) {
return funcs[0]
}
return funcs.reduce((a, b) => (...args) => a(b(...args)))
}
// actionTypes.js
const ActionTypes = {
INIT: `@@redux/INIT${
randomString()}`, // store 初始化所触发的 action
REPLACE: `@@redux/REPLACE${
randomString()}`, // store 中 reducer 被热替换所触发的 action
PROBE_UNKNOWN_ACTION: () => `@@redux/PROBE_UNKNOWN_ACTION${
randomString()}` // 未知的 action
}
actionTypes 中所定义的三个预设 action 主要在 createStore
以及 combineReducers
中有所使用,详见后续章节内容。
CreateStore
createStore
是Redux所提供的API之一,其用于生成唯一的 store 并提供对应的 dispatch
、subscribe
等方法。Redux 中的 store 仅能通过 dispatch
函数触发对应的 action 来改变,action 对应到纯函数的 Reducer 从而修改 store 的数据。createStore
的一个使用示例如下:
let store = createStore(myReducer, {
}, applyMiddleware(thunk, logger));
createStore
接收三个参数:reducer, preloadedState, enhancer
,其中reducer
参数表示对store进行修改的 reducer 函数,其可由combineReducers
函数组合多个 reducer 生成。preloadedState
代表 store 的初始化状态,该参数可以省略,当其被省略时,输入参数为:reducer, enhancer
。enhancer
是中间件通过applyMiddleware
方法所生成的对createStore
进行功能加强的函数。具体代码解析如下:
export default function createStore(reducer, preloadedState, enhancer) {
// 处理传入多个 enhancer 的错误
if (
(typeof preloadedState === 'function' && typeof enhancer === 'function') ||
(typeof enhancer === 'function' && typeof arguments[3] === 'function')
) {
throw new Error('balabala')
}
// 当传入两个参数时,其输入参数的含义为 (reducer, enhancer)
if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
enhancer = preloadedState
preloadedState = undefined
}
if (typeof enhancer !== 'undefined') {
// enhancer 必须是一个函数
if (typeof enhancer !== 'function') {
throw new Error('Expected the enhancer to be a function.')
}
// 使用 enhancer 对 createStore 功能进行加强,enhancer 的具体执行功能见 applyMiddlerware
return enhancer(createStore)(reducer, preloadedState)
}
// reducer 同样必须为函数,为什么不把这一步判断提前到 enhancer 前头?
// 这样不就留下了到 enhancer 执行完后再处理了吗?
if