redux源码解析

38次阅读

共计 1895 个字符,预计需要花费 5 分钟才能阅读完成。

前言

redux 的源码是我阅读过的一些库的源码中,相对简单的。如果大家的感兴趣强烈推荐大家亲自阅读一下。

本文为了方便理解 抛开了一些容错处理以及边缘条件的判断

combineReducers

combineReducers是 redux 中内置的工具函数,目的是将多个 reducer 函数合并为一个最终的 reducer 函数。这个最终的 reducer 函数可以用于 createStore 中作为参数。

下面两种写法是完全等价的。

combineReducers 的实现非常的简单。在 A 处首先对 reducers 对象进行遍历,排除 value 值的类型不是 function 的 value。

B 处,我们会遍历经过前一步过滤的 reducers 对象,依次的执行 reducers 对象中每一个 reducer 函数, 将返回的结果存储在新的对象 nextState 中,最后返回新的对象。

createStore

createStore, 会创建一个 Store, 存放应用中全部的 state, 形成 state 树。

另外 Store 会提供额外的四个方法。getState 获取 Store 存储的 state 树;dispatch 分发 action 更改 Store 中的 state;subscribe 注册监听器会在 dispatch 时触发;replaceReducer 替换用来计算 state 的 reducer。

createStore, 接收 3 个参数:

  • reducer,负责处理 action,返回新的 state 树。
  • preloadedState,初始的 state。如果是通过 combineReducers 创建 reducer,初始的 preloadedState 的 keys 必须与 reducers 对象保持一致。
  • enhancer,store 增强器,enhancer 是一个高阶函数,返回值是一个经过包装的强化的 store。而 redux 的 applyMiddleware 本身就是一个 enhancer。

dispatch

dispatch 将会用来分发 action, 更新 currentState 对象。在更新完成后,同时会更新 currentListeners,并依次执行监听者列表。

getState

replaceReducer

使用新的 reducer 替换现有的 reducer,同时执行dispatch({type: ActionTypes.REPLACE})(ActionTypes.REPLACE 是随机的字符串)。初始化 state。

subscribe

subscribe 会为 dispatch 注册监听器,监听器存储在 nextListeners 数组中,subscribe 返回的函数则会注销监听器。

compose

compose 并不是 redux 中的概念,而是函数式编程中概念。类似的方法在 ramda 等工具库均有实现。

从右往左执行函数组合(右侧函数的输出作为左侧函数的输入)。最右侧函数可以是多参函数,其余函数必须是单参函数。类似 a(b(c(arg)))。

middleware

redux 的中间件的模型类似与 koa。在 next 前面以及 next,由外向里依次执行。当最里层的 next 执行完成后,next 后面的代码,会由内向外依次执行。非常类似 koa 的洋葱中间件模型。

以下是一个简单的 redux 中间件的示例。

下面是 redux 文档中, 为介绍中间件的原理而给出的 applyMiddleware 的 单纯的实现

中间件会对 dispatch 进行一层包装,并且总是会返回包装后的 dispath。下一个中间件,会基于上一个中间件返回的 dispatch 再次进行处理。

applyMiddleware

在前面我们说过 applyMiddleware 是 redux 内置的 enhancer。我们先来回顾一下 enhancer 的使用方法。

在 createStore 中调用 enhancer。参数为 createStore 自身,enhancer 会返回一个新的函数。接收 reducer, preloadedState 对象作为参数。

在 applyMiddleware 中,利用 js 的闭包的特性使用 createStore 以及 reducer, preloadedState 参数创建 store。

使用 管道 compose,将 store.dispatch 逐层的进行包装????,返回的 dispath 会覆盖 store 中 dispatch。

bindActionCreators

bindActionCreators 在平时工作中出镜率很少,bindActionCreators 主要用处是将 dispatch 方法包装到 action creator 中。bindActionCreators 的源码很简单。下面是具体实现。

正文完
 0

redux源码解析

38次阅读

共计 5366 个字符,预计需要花费 14 分钟才能阅读完成。

1. 前言
关于 redux 的基本概念和工作流如何进行的这里就不进行过多概述了,可以查看相关文档去了解。流程图链接
2.redux 源码结构
以下是 redux 的源码结构图,主要的就是以下几个文件组成,我们接下来按顺序进行介绍其中原理和实现过程。
3.createStore.js
首先了解下 createStore.js。通过调用 createStore 创建唯一的 store,store 中暴露出 getState,dispatch,subscribe,replaceReducer 这几个方法。通常我们用到的主要是前三个方法,这里作为主要介绍内容。如下是 createStore 的主要内容:
export function createStore(reducer, preloadedState, enhancer) {
/**
* 以下的判断都是对传入的参数进行验证
*/
if(
(typeof preloadedState === ‘function’ && typeof enhancer === ‘function’) ||
(typeof enhancer === ‘function’ && typeof arguments[3] === ‘function’)
) {
throw new Error(‘ 只能传递一个 enhancer 到 createStore() 中 ’)
}

if(typeof preloadedState === ‘function’ && typeof enhancer === ‘undefined’) {
enhancer = preloadedState
preloadedState = undefined
}

if(typeof enhancer !== ‘undefined’) {
if(typeof enhancer !== ‘function’) {
throw new Error(‘enhancer 应该为一个函数 ’)
}

return enhancer(createStore)(reducer, preloadedState)
}

if(typeof reducer !== ‘function’) {
throw new Error(‘reducer 应该为一个函数 ’)
}

/**
* 初始化参数
*/
let currentReducer = reducer // 初始化 reducer
let currentState = preloadedState // 初始化 state
let currentListeners = [] // 初始化 subscribe 监听函数数组
let nextListeners = currentListeners
let isDispatching = false

/**
* 复制一份 currentListeners,为了防止在 dispatch 的时候
* 调用 subscribe 和 unsubscribe 时候发生错误
*/
function ensureCanMutateNextListeners() {
if(nextListeners === currentListeners) {
nextListeners = currentListeners.slice()
}
}

/**
* 获取当前的 state
*/
function getState() {
if(isDispatching) {
throw new Error(‘ 不可以在 isDispatching 的时候调用 getState’)
}
return currentState
}

/**
* 订阅监听事件,触发 dispatch 后执行
*/
function subscribe(listener) {
if(typeof listener != ‘function’) {
throw new Error(‘Expected the listener to be a function.’)
}

if(isDispatching) {
throw new Error(‘isDispatching 的时候无法调用 ’)
}

let isSubscribed = true
ensureCanMutateNextListeners()
nextListeners.push(listener)

return function unsubscribe() {
if(!isSubscribed) {// 正在解除监听事件的时候不向下执行
return
}
if(isDispatching) {
throw new Error(‘ 正在 dispatch 的时候不给执行 ’)
}
isSubscribed = false
ensureCanMutateNextListeners()
const index = nextListeners.indexOf(listener)
nextListeners.splice(index)
}
}

/**
* 执行好 dispatch 循环调用每个 subscribe 的函数
*/
function dispatch() {
// 关于验证的代码就不写了
const listeners = (currentListeners = nextListeners)
for(let i=0; i<listeners.length; i++) {
listeners[i]()
}
return action
}

/**
* 替换当前的 reducer 然后重新初始化一次 dispatch
*/
function replaceReducer(nextReducer) {
currentReducer = nextReducer
dispatch({type: ‘@INITACTION’})
}

// 初始化执行 dispatch
dispatch({type: ‘@INITACTION’})
}
4. combineReducers.js
combineReducers,它接收多个 reducer 函数,并整合,归一化成一个 rootReducer。其返回值 rootReducer 将会成为 createStore 的参数,完成 store 的创建。combineReducers 只接收一个参数,这个参数阐述了不同 reducer 函数和页面状态数据树不同部分的映射匹配关系。
const combineReducers = (reducers) => {
return (state={}, action) => {
Object.keys(reducers).reduce((nextState, key) => {
nextState[key] = reducers[key](state[key], action)
return nextState
}, {})
}
}
5. applyMiddleware.js
可以通过此方法给 redux 在触发 action 到 reducer 的过程中增加一个中间环节。applyMiddleware 返回的内容我们称为 enhancer。这个是 createStore 方法的最后一个参数,并且是可选的。在 redux 源码中涉及中间件的脚本有 applyMiddleware.js、createStore.js、compose.js。那么 applyMiddleware(…middlewares) 中会发生什么事情。在 createStore.js 中有一段源码如下:
export default function createStore(reducer, preloadedState, enhancer) {
//…
return enhancer(createStore)(reducer, preloadedState)
//…
}
顾名思义,applyMiddleware 就是对各个需要的中间件进行糅合,并作为 createStore 的第二个或者第三个参数传入。用于增强 store。源码如下:
const combineReducers = (reducers) => {
return (state = {}, action) => {
return Object.keys(reducers).reduce((nextState, key) => {
nextState[key] = reducers[key](state[key], action)
return nextState
}, {})
}
}

export default function applyMiddleware(…middlewares) {
return (next) => {
return (reducer, initialState) => {
var store = next(reducer, initialState)
var dispatch = store.dispatch
var chain = []

// 包装一下 store 的 getState 和 dispatch 方法
// 是第三方中间件需要使用的参数

var middlewareAPI = {
getState: store.getState,
dispatch: (action) => dispatch(action)
}
// 每个中间件也是一个高度柯里化的函数,它接收 middlewareAPI 参数后的第一次返回结果并存储到 chain 数组中
//chain 数组中每一项都是对 dispatch 的增强,并进行控制权转移。
chain = middlewares.map(middleware => middleware(middlewareAPI))
// 这里的 dispatch 函数就是增强后的 dispatch,因此 compose 方法接收了 chain 数组和原始 dispatch 方法。
dispatch = compose(…chain, store.dispatch)
return {
…store,
dispatch
}
}
}
}

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)))
}
6. compose.js
这个方法在 applymiddleware 中介绍了,可以在上面看到。
7.bindActionCreators.js
这个模块涉及的内容较少,我们直接去看源码:
function bindActionCreator(actionCreator, dispatch) {
// 这个函数主要作用就是返回一个函数,当我们调用返回的这个函数的时候
// 会自动的 dispatch 对应的 action
return function() {
return dispatch(actionCreator.apply(this, args))
}
}
/**
参数说明:
actionCreators: action create 函数,可以是一个单函数,也可以是一个对象,这个对象的所有元素都是 action create 函数
dispatch: store.dispatch 方法
*/
export default function bindActionCreators(actionCreators, dispatch) {
// 如果 actionCreators 是一个函数的话,就调用 bindActionCreator 方法对 action create 函数和 dispatch 进行绑定
if (typeof actionCreators === ‘function’) {
return bindActionCreator(actionCreators, dispatch)
}
// actionCreators 必须是函数或者对象中的一种,且不能是 null
if (typeof actionCreators !== ‘object’ || actionCreators === null) {
throw new Error(
`bindActionCreators expected an object or a function, instead received ${actionCreators === null ? ‘null’ : typeof actionCreators}. ` +
`Did you write “import ActionCreators from” instead of “import * as ActionCreators from”?`
)
}

// 获取所有 action create 函数的名字
const keys = Object.keys(actionCreators)
// 保存 dispatch 和 action create 函数进行绑定之后的集合
const boundActionCreators = {}
for (let i = 0; i < keys.length; i++) {
const key = keys[i]
const actionCreator = actionCreators[key]
// 排除值不是函数的 action create
if (typeof actionCreator === ‘function’) {
// 进行绑定
boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)
}
}
// 返回绑定之后的对象
/**
boundActionCreators 的基本形式就是
{
actionCreator: function() {dispatch(actionCreator.apply(this, arguments))}
}
*/
return boundActionCreators
}

正文完
 0