Redux(1)源码解析

预备知识

先提出个疑问:我们为什么需要状态管理?

对于SPA应用来说,前端所需要管理的状态越来越多,需要查询、更新、传递的状态也越来越多,如果让每个组件都存储自身相关的状态,理论上来讲不会影响应用的运行,但在开发及后续维护阶段,我们将花费大量精力去查询状态的变化过程,在多组合组件通信或客户端与服务端有较多交互过程中,我们往往需要去更新、维护并监听每一个组件的状态,在这种情况下,如果有一种可以对状态做集中管理的地方是不是会更好呢?状态管理好比是一个集中在一处的配置箱,当需要更新状态的时候,我们仅对这个黑箱进行输入,而不用去关心状态是如何分发到每一个组件内部的,这可以让开发者将精力更好的放在业务逻辑上。

但状态管理并不是必需品,当你的UI层比较简单、没有较多的交互去改变状态的场景下,使用状态管理方式反倒会让你的项目变的复杂。例如Redux的发明者Dan Abramov 就说过这样一句话:“只有遇到 React 实在解决不了的问题,你才需要Redux”。

一般来讲,在以下场景下你或许需要使用状态管理机制去维护应用:

  1. 用户操作较为繁琐,导致组件间需要有状态依赖关系,如根据多筛选条件来控制其他组件的功能。
  2. 客户端权限较多且有不同的使用方式,如管理层、普通层级等。
  3. 客户端与服务端有大量交互,例如请求信息实时性要求较高导致需要保证鲜活度。
  4. 前端数据缓存部分较多,如记录用户对表单的提交前操作、分页控制等。
    在这里插入图片描述举个最简单的例子:
//创建一个最基本的store
const store =createStore(reducers);

// subscribe() 返回一个函数用来注销监听器
const unsubscribe = store.subscribe(()=>console.log(store.getState()))

// 发起一系列 action
store.dispatch(addTodo('Learn about actions'))
store.dispatch(addTodo('Learn about reducers'))

通过以上几句代码,我们已经实现了数据流从dispatch(action)->reducer->subscribe->view回调的整体流程(此处省略了middleWare的部分),在这个例子中没有任何的UI层,redux也同样可以独立完成完整的数据流向。其中subscribe是对state变化更新的订阅功能,可以在回调函数中注册view渲染功能。

使用规范

单一数据源
我们可以把Redux的状态管理理解成一个全局对象,那么这个全局对象是唯一的,所有的状态都在全局对象store下进行统一”配置”,这样做也是为了做统一管理,便于调试与维护。
State是只读的
与React的setState相似,直接改变组件的state是不会触发render进行渲染组件的。同样,在Redux中唯一改变state的方法就是触发action,action是一个用于描述发生了什么的“关键词”,而具体使action在state上更新生效的是reducer,用来描述事件发生的详细过程,reducer充当了发起一个action连接到state的桥梁。这样做的好处是当开发者试图去修改状态时,Redux会记录这个动作是什么类型的、具体完成了什么功能等(更新、传播过程),在调试阶段可以为开发者提供完整的数据流路径。
Reducer必须是一个纯函数
Reducer用来描述action如何改变state,接收旧的state和action,返回新的state。Reducer内部的执行操作必须是无副作用的,不能对state进行直接修改,当状态发生变化时,需要返回一个全新的对象代表新的state。这样做的好处是,状态的更新是可预测的,另外,这与Redux的比较分发机制相关,阅读Redux判断状态更新的源码部分(combineReducers),发现Redux是对新旧state直接用来进行比较,也就是浅比较,如果我们直接在state对象上进行修改,那么state所分配的内存地址其实是没有变化的,“”是比较对象间的内存地址,因此Redux将不会响应我们的更新。之所以这样处理是避免对象深层次比较所带来的性能损耗(需要递归遍历比较)。

在使用Redux时,我们只要严格按照以上三种约定,就可以避免大部分不必要的bug。

核心源码剖析

Redux官方代码库提供了以下几个模块文件:
applyMiddleware.js
bindActionCreators.js
combineReducers.js
compose.jscreateStore.js
针对以上模块,我们从易到难展开理解:

compose.js

从右到左进行函数式编程

/**
 * Composes single-argument functions from right to left. The rightmost
 * function can take multiple arguments as it provides the signature for
 * the resulting composite function.
 *
 * @param {...Function} funcs The functions to compose.
 * @returns {Function} A function obtained by composing the argument functions
 * from right to left. For example, compose(f, g, h) is identical to doing
 * (...args) => f(g(h(...args))).
 */
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)))
} 

以上代码很好理解,当compose无参数时,返回一个空函数,参数为唯一函数时,直接将这个函数作为返回值,重点在于最后一部分:

return funcs.reduce((a, b) => (...args) => a(b(...args))) 

对多个参数组合成的函数数组进行reduce操作,其实以上代码等同于:

return funcs.reduceRight((composed, f) => f(composed));

相当于对数组内的所有函数,从右至左,将前一个函数作为后一个函数的入口参数依次返回,比如compose(fn1,fn2,fn3)最后返回的结果应该是这样子的:

fn1(fn2(fn3))
bindActionCreators.js

bindActionCreator将值为actionCreator的对象转化成具有相同键值的对象,每一个actionCreator都会被dispatch所包裹调用,因此可以直接使用,简而言之,bindActionCreator可以让开发者在不直接接触dispacth的前提下进行更改state的操作,下面我们来看下它是如何实现的:

import warning from'./utils/warning'

function bindActionCreator (actionCreator, dispatch) {
   
    return (...args) => dispatch(actionCreator(...args))
}

export default function bindActionCreators (actionCreators, dispatch) {
   
    if (typeof actionCreators ==='function') {
   
        return bindActionCreator(actionCreators
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值