一、Redux
前端框架->React->插件
在MVC的世界里,React相当于View的部分,只涉及到页面的渲染,一旦需要涉及到应用的数据管理,还是需要交给Model和Controller,但是Redux并不是一个MVC框架,它用一种新的思路来管理数据。
MVC
MVC是业界广泛接受的一种前端应用框架类型,这种框架把应用分为三个部分:
Model(模型)负责管理数据,大部分业务逻辑应该放在Model中
View(视图)负责渲染用户页面,应该避免在View中涉及业务逻辑
Controller(控制器)负责接受用户输入,根据用户输入调用相应的Model部分逻辑,把产生的数据结果交给View部分,让View渲染出必要的输出
MVC框架提出的数据流很理想,用户请求先到达Controller,由Controller调用Model获得数据,然后把数据交给View。但是,在实际框架实现中,总是允许View和Model直接通信
然而,在MVC中让View和Model直接对话就是灾难
Flux
【优势】
在Flux中,Store只有get方法,没有set方法,根本不可能直接去修改其内部状态,View只能通过get方法获取Store的状态,无法直接去修改状态,如果View想要修改Store的状态,只能派发一个action对象给Dispatcher
【不足】
1、Store之间依赖关系
在Flux的体系中,如果两个Store之间有逻辑依赖关系,就必须用上Dispatcher的waitFor函数
2、难以进行服务器端渲染
3、Store混杂了逻辑和状态
Redux
Redux的含义是Reducer+Flux。Reducer是一个计算机科学中的通用概念。以Javascript为例,数组类型有reduce函数,接受的参数是一个reducer,reducer做的事情就是把数组所有元素依次做规约,对每个元素都调用一次参数reducer,通过reducer函数完成规约所有元素的功能
Flux的基本原则是单向数据流,Redux在此基础上强调三个基本原则:
1、唯一数据源
2、保持状态只读
3、数据改变只通过纯函数完成
Redux
Redux的含义是Reducer+Flux。Reducer是一个计算机科学中的通用概念。以Javascript为例,数组类型有reduce函数,接受的参数是一个reducer,reducer做的事情就是把数组所有元素依次做规约,对每个元素都调用一次参数reducer,通过reducer函数完成规约所有元素的功能
Flux的基本原则是单向数据流,Redux在此基础上强调三个基本原则:
1、唯一数据源
2、保持状态只读
3、数据改变只通过纯函数完成
容器和展示
一个React组件基本上要完成以下两个功能:
1、读取Store的状态,用于初始化组件的状态,同时还要监听Store的状态改变;当Store状态发生变化时,需要更新组件状态,从而驱动组件重新渲染;当需要更新Store状态时,就要派发action对象
2、根据当前props和state,渲染出用户界面
让一个组件只专注做一件事。于是,按照这两个功能拆分成两个组件。这两个组件是父子组件的关系。业界对于这样的拆分有多种叫法,承担第一个任务的组件,也就是负责和redux打交道的组件,处于外层,被称为容器组件;只专业负责渲染界面的组件,处于内层,叫做展示组件
展示组件,又称为傻瓜组件,就是一个纯函数,根据props产生结果。实际上,让展示组件无状态,只根据props来渲染结果,是拆分的主要目的之一。状态全部交给容器组件去处理
或者,直接使用解构赋值的方法
React-redux
react-redux遵循将组件分成展示组件和容器组件的规范。react-redux提供了两个功能:
1、Provider组件,可以让容器组件默认可以取得state,而不用当容器组件层级很深时,一级级将state传下去
2、connect方法,用于从展示组件生成容器组件。connect的意思就是将这两种组件连接起来
模块化应用
从架构出发,开始一个新应用时,有几件事情是一定要考虑清楚的:
1、代码文件的组织结构
2、确定模块的边界
3、Store的状态树设计
【代码文件的组织结构】
Redux应用适合于按功能组织,也就是把完成同一应用功能的代码放在一个目录下,一个应用功能包含多个角色的代码。在Redux中,不同的角色就是reducer、actions和视图。而应用功能对应的就是用户界面上的交互模块
以Todo应用为例,这个应用的两个基本功能就是TodoList和Filter,所以代码可以这样组织:
【模块接口】
不同功能模块之间的依赖关系应该简单而清晰,也就是所谓的保持模块之间低耦合性;一个模块应该把自己的功能封装得很好,让外界不要太依赖于自己内部的结构,这样不会因为内部的变化而影响外部模块的功能,这就是所谓的高内聚性
【状态树的设计】
状态树的设计需要遵循如下几个原则:
1、一个模块控制一个状态节点
2、避免冗余数据
3、树形结构扁平 (避免依赖)
对于Todo应用的状态树设计如下
reselect
reselect库的原理是只要相关状态没有改变,那就直接使用上一次的缓存结果。reselect用来创造选择器,接收一个state作为参数的函数,返回的数据是某个mapStateToProps需要的结果
首先,安装reselect库
npm install --save reselect
reselect提供了创造选择器的createSelector函数,这是一个高阶函数,也就是接受函数为参数来产生一个新函数的函数
createSelector 接收一个 input-selectors 数组和一个转换函数作为参数。如果 state tree 的改变会引起 input-selector 值变化,那么 selector 会调用转换函数,传入 input-selectors 作为参数,并返回结果。如果 input-selectors 的值和前一次的一样,它将会直接返回前一次计算的数据,而不会再调用一次转换函数。
常见错误
在使用redux的过程中,会出现如下的常见错误
【错误:reducers不能触发actions】
Uncaught Error: Reducers may not dispatch actions.
一般来说,出现"Reducedrs may not dispatch actions"的错误,是因为reducer中出现路由跳转语句,而跳转到的语句正好发送了dispatch。从而,reducer不再是纯函数
【redux中的state发生变化,但页面没有重新渲染】
一般地,是因为展开运算符使用不当所至
对于对象的展开运算符,需要把...state放到第一个条目位置,因为后面的条目会覆盖展开的部分
return {...item,completed:!item.completed}
【reducer中不能使用undefined】
1、reducer中state不能返回undefined,可以用null代替