Redux 学习纪要

背景

Redux 是 React 全家桶的重要一员,理解 Redux 可以更好的帮助我们开发前端项目。本文只谈论概念的解读,不讲解具体的用法。

Redux 和 React-Redux

首先声明 Redux 和 React-Redux 的区别。 Redux 这个项目严格来说,并不局限于 React,它可以用于 ES6, Angular ,React。而 React-Redux 是 React 官方提供的一个集成 Redux 的包。

为什么要 Redux

这个要从 React 的哲学讲起,在 React 中,有两个重要的概念 props 和 state,
props 一般是外部组件传入的,而 state 是组件内部的状态。也就是说数据从外部更大的组件传递到内部被包含的数据。

React 中的数据流向

这样的方式,非常直观,对于一些平铺式的页面非常有优势,比如资讯类应用,资讯只需要瀑布流式的展示,资讯之间没有交互。

可是并不是所有的页面都那么简单,也有很多场景,我们页面组件之间还是需要交互的,这个时候就不一样了。组件之间的关系是平级的,没法直接传递数据的。

组件无法直接传递数据

没有 Redux 的情况

针对非包含关系的组件间传递数据的问题,React 官方文档提到了一种解决办法,见 Lifting State Up, 其思路其实是添加一个组件,我们暂时称它为外包组件,让它包含需要交互的两个组件,然后通过被包含的组件触发 callback 方法,引起外包组件变化,然后外包组件再修改另一个组件。

callback 方式传递数据

这当然是解决问题的一种方式,但是这种方式却会带来一个问题,因为一旦需要交互的组件最近公共外包组件特别远的话,需要大量地修改代码,一层一层的传递 callback 函数。这样的工作量简直不要太大。

当组件支架的最近公共外包组件很远的时候,callback简直崩溃

Redux 是怎么做的

在写 React 的时候,通常我看到一个前端组件的时候,我们总是很自然的认为组件应该包含 view 和 state 两个部分。组件应该高内聚的维护其 view 和 state。 但是 Redux 不这么思考, Redux 认为组件最好只维护 view , 而 state 应该在一个地方集中的维护。

redex 数据集中化管理图示

Redux 将所有的数据,集中在一个地方,组件可以直接引用这个数据源,同样也有了修改这个公共数据源的能力。通过这样的方式解决了组件间数据流动的问题。

Redux 中重要的概念

知道了 Redux 做了什么,现在我们来看看 Redux 为了实现这些功能,都提供了哪些概念。

Store

store 就是存储全局数据的地方。组件可以通过连接到 store 来获取到全局的数据。

Action

当组件可以通过连接 store 来查看和修改全局数据,用于触发变更。但是比起直接修改 store,更好的方法是使用命令模式,组件只需要发布命令就好了,然后让具体的执行者去执行,这样就可以将命令和执行解耦开。 Redux 使用了这样的设计模式,组件发送命令,然后由 Reducers 去执行具体的命令。而 Redux 的 action 正是命令模式中的 command 。action 中包含命令的类型 type 和命令的内容。

Reducers

Reducers 就是命令模式中的命令接受者,Reducers 接受到 action,然后根据 action 的内容去对 store 执行相应的变更操作。

pure function (纯函数)

Reducers 的具体实现其实就是一个大的函数,Redux 官方文档声明成为 Reducers 的函数必须是一个 pure funciton。所谓纯函数和我们常说的无副作用函数是有区别的。

纯函数: 在任何时刻,对于给定的一个入参,只有唯一一个对应的结果

根据上述的定义,类似 f(x) = x + 1。 这样简单的函数就是纯函数,纯函数最主要的概念是将时间区分出来了,所以类似向服务端发起请求获取文章 pv 的接口就不是纯函数,因为 pv 会随着时间的推移而发生改变。之所以要求是纯函数,据 Redux 作者在自己的博客 The Evolution of Flux Frameworks 中所说的

Stores and actions are just pure functions. They are easily testable in isolation.

主要是为了易于测试,因为纯函数便于做重放操作。

dispatch

顾名思义,就是分发器方法,负责将 action 分发到 Reducers 。

subsribe

订阅方法,参数是监听器,监听 store 的变更事件,当 store 发生变化的时候,触发回调函数

connect

这个方法功能主要的是将当前组件和全局的 store 连接,并将 dispatch 注入到当前的组件的 props 中。 当 store 更新的时候,会通过 connect 传入的回调函数将更新后的数据通知到当前组件。而且由于 connect 可以方便的指定当前组件需要关心的数据,所以用了 connect 后, subsribe 基本上就没有使用必要了。

Redux 数据流程

Redux 数据流程

Store 与 render 的触发

当 store 发生变化的时候,会通过 connect 中的回调函数触发组件重新 render,以此来改变页面内容。

Store 的局部改变

当 store 发生改变的时候,改变的数据可能只是局部的一小部分,当小部分数据发生变更的时候,redux 会只更新局部的 view ,而其它不发生改变,这样有助于提升性能。

如何判断数据是否更新

数据要更新了才能触发 render,但是如何判断数据已经发生改变了呢。众所周知,对于嵌套层次特别深的数据,很难判断数据是否 update,而且如果要判断,必须递归地判断,这样性能是比较差的,所以 react-redux 使用了浅层判断的方式,类比 C++ 和 Java 其实就是判断对象的内存地址,在 JavaScript 中其实就是 == 判断。

嵌套判断

因为使用了浅层判断,所以对于一个数据,比如 a.b.c.d ,如果我们要更新 d 数据,如果我们只更新了 d,是不够的,因为 a 没有更新,无法通过浅层判断。但是仅仅更新 a,d 也不够,因为到了 b 依旧无法通过浅层判断,所以如果要更新d ,则 d,c,b,a 都要更新,也即更新一个数据,其所有的父对象也都要更新。
这个要特别关注,因为经常出现的页面没有重新 render 的原因,大多是因为没有重新生成父对象。

Redux 和传统的 发布/订阅 系统

虽然 Redux 中也有发布和订阅,但和我们通常使用的基于观察者模式的 发布/订阅系统却有所不同。

Redux 添加组件不符合开闭原则

我们常用的发布/订阅模型,订阅者向消息中心订阅消息,然后收到消息,在这个过程中,不需要修改现有代码,而在 Redux 中添加组件,不仅需要修改初始化 store,还需要修改 redurces ,用于配合修改新添加的组件的 state。在这个过程中存在代码修改,不符合开闭原则。

没有消息堆积功能

Redux 中没有原生的消息堆积的概念。

组件不符合内聚性

使用了 Redux 的组件,因为其和全部变量绑定,也就意味着和整个应用紧耦合了,这是不符合高内聚原则的,也即意味着使用了 Redux 的组件跨应用的复用性差,所以 Redux 更适合开发有复杂交互的 webApp,而不适合用于开发需要高可复用性的组件。如果自己开发的组件也有一定的交互,可以考虑自行开发一套简易的发布/订阅工具。我在我自己的项目中,开发的组件,为了能不用 redux 实现消息的订阅和发布,自行开发了如下简易的发布/订阅工具

const dispatchMap = [];

export function registerListener(topic,callback,tags = '*'){
    let listener = {
        topic: topic,
        tags: tags,
        callback: callback
    };
    dispatchMap.push(listener);
}

export function publish(msg){
    dispatchMap.forEach( (listener) => {
        if(listener.topic == msg.topic){
            if(listener.tags === '*' || listener.tags.includes(msg.tag)){
                listener.callback(msg.content);
            }
        }
    });
}

总结

redux 之所以能在 react 中流行,归根结底是因为它能解决 react 中数据流动性差的问题,搞明白这点之后,很多 redux 的设计也就能理解了。所以说学习一个新的东西,我认为最重要的部分,是先学习我们为什么需要它,当我们搞明白为什么需要它之后,然后再学习它是怎么解决的,就会少很多疑惑,不会一来看到一大把术语而不知所措。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值