1. 为什么使用Redux
?
React
是视图框架。Redux
是一个用来管理数据状态和**UI
状态**的JavaScript
应用工具。
随着 JavaScript
单页应用(SPA)开发日趋复杂, JavaScript
需要管理比任何时候都要多的state
(状态), Redux
就是降低管理难度的。(Redux
支持React
、Angular
、jQuery
甚至纯JavaScript
)。
在 React
中,UI 以组件
的形式来搭建,组件之间可以嵌套组合。但 React
中组件间通信的数据流是单向的,顶层组件可以通过 props
属性向下层组件传递数据,而下层组件不能向上层组件传递数据,兄弟组件之间同样不能。这样简单的单向数据流支撑起了 React
中的数据可控性。
Redux
提供了一个叫 store
的统一仓储库,组件通过 dispatch
将 state
直接传入store
,不用通过其他的组件。并且组件通过 subscribe
从 store
获取到 state
的改变。使用了 Redux
,所有的组件都可以从 store
中获取到所需的 state
,他们也能从store
获取到 state
的改变。
2. Redux主要解决的问题:
单纯的Redux只是一个状态机,是没有UI呈现的,React-Redux
作用是将Redux
的状态机和React
的UI
呈现绑定在一起,当你dispatch
action
改变state
的时候,会自动更新页面。
3. Redux 工作流程
1. const store = createStore(fn); //生成数据;
2. action: { type: Symble('action01), payload:'payload' };//定义行为;
3. dispatch派发action: store.dispatch(doSomething('action001'));
4. reducer:处理action,返回新的state;
解释:
- 首先,用户(通过View)发出 Action,发出方式就用到了dispatch方法
- 然后,Store自动调用Reducer,并且传入两个参数:当前State和收到的Action,然后Reducer会返回新的State。
- State—旦有变化,Store就会调用监听函数,来更新View。
以 store 为核心,可以把它看成数据存储中心,但是他要更改数据的时候不能直接修改,数据修改更新的角色由Reducers
来担任,store只做存储,中间人;当Reducers
的更新完成以后会通过store的订阅来通知react component,组件把新的状态重新获取渲染,组件中也能主动发送action,创建action后这个动作是不会执行的,所以要dispatch这个action,让store通过reducers去做更新React Component 就是react的每个组件。
4. Redux 中异步的请求怎么处理
- 可以在
componentDidmount
中直接进⾏请求⽆须借助redux。 - 异步中间件:在⼀定规模的项⽬中,上述⽅法很难进⾏异步流的管理,通常情况下我们会借助redux的异步中间件进⾏异步处理。redux异步流中间件其实有很多,当下主流的异步中间件有两种
redux-thunk
、redux-saga
。
4.1 使用react-thunk中间件
redux-thunk优点:
- 体积⼩: redux-thunk的实现⽅式很简单,源码只有不到20⾏代码
- 使⽤简单: redux-thunk没有引⼊像redux-saga或者redux-observable额外的范式,上⼿简单
redux-thunk缺陷:
- 样板代码过多: 与redux本身⼀样,通常⼀个请求需要⼤量的代码,⽽且很多都是重复性质的
- 耦合严重: 异步操作与redux的action偶合在⼀起,不⽅便管理
- 功能孱弱: 有⼀些实际开发中常⽤的功能需要⾃⼰进⾏封装
使用步骤:
- 配置中间件,在store的创建中配置:
import {createStore, applyMiddleware, compose} from 'redux';
import reducer from './reducer';
import thunk from 'redux-thunk'
// 设置调试工具
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
// 设置中间件
const enhancer = composeEnhancers(
applyMiddleware(thunk)
);
const store = createStore(reducer, enhancer);
export default store;
- 添加一个返回函数的
actionCreator
,将异步请求逻辑放在里面:
/**
发送get请求,并生成相应action,更新store的函数
@param url {string} 请求地址
@param func {function} 真正需要生成的action对应的actionCreator
@return {function}
*/
// dispatch为自动接收的store.dispatch函数
export const getHttpAction = (url, func) => (dispatch) => {
axios.get(url).then(function(res){
const action = func(res.data)
dispatch(action)
})
}
- 生成
action
,并发送action
componentDidMount(){
var action = getHttpAction('/getData', getInitTodoItemAction)
// 发送函数类型的action时,该action的函数体会自动执行
store.dispatch(action)
}
5. Redux 怎么实现属性传递,介绍下原理
react-redux
数据传输∶ view–>action–>reducer–>store–>view。看下点击事件的数据是如何通过redux传到view上:
1. view 上的AddClick 事件通过mapDispatchToProps 把数据传到action -> click:()=>dispatch(ADD)
2. action 的ADD 传到reducer上
3. reducer传到store上 const store = createStore(reducer);
4. store再通过 mapStateToProps 映射穿到view上text:State.text
代码示例∶
import React from 'react';
import ReactDOM from 'react-dom';
import { createStore } from 'redux';
import { Provider, connect } from 'react-redux';
class App extends React.Component{
render(){
let { text, click, clickR } = this.props;
return(
<div>
<div>数据:已有人{text}</div>
<div onClick={click}>加人</div>
<div onClick={clickR}>减人</div>
</div>
)
}
}
const initialState = {
text:5
}
const reducer = function(state,action){
switch(action.type){
case 'ADD':
return {text:state.text+1}
case 'REMOVE':
return {text:state.text-1}
default:
return initialState;
}
}
let ADD = {
type:'ADD'
}
let Remove = {
type:'REMOVE'
}
const store = createStore(reducer);
let mapStateToProps = function (state){
return{
text:state.text
}
}
let mapDispatchToProps = function(dispatch){
return{
click:()=>dispatch(ADD),
clickR:()=>dispatch(Remove)
}
}
const App1 = connect(mapStateToProps,mapDispatchToProps)(App);
ReactDOM.render(
<Provider store = {store}>
<App1></App1>
</Provider>,document.getElementById('root')
)
6. Redux 中间件是什么?接受几个参数?柯里化函数两端的参数具体是什么?
Redux 的中间件提供的是位于 action 被发起之后,到达 reducer 之前的扩展点,换而言之,原本 view ->> action -> reducer -> store 的数据流加上中间件后变成了 view -> action -> middleware -> reducer -> store ,在这一环节可以做一些"副作用"的操作,如异步请求、打印日志等。
applyMiddleware源码:
export default function applyMiddleware(...middlewares) {
return createStore => (...args) => {
// 利用传入的createStore和reducer和创建一个store
const store = createStore(...args)
let dispatch = () => {
throw new Error()
}
const middlewareAPI = {
getState: store.getState,
dispatch: (...args) => dispatch(...args)
}
// 让每个 middleware 带着 middlewareAPI 这个参数分别执行一遍
const chain = middlewares.map(middleware => middleware(middlewareAPI))
// 接着 compose 将 chain 中的所有匿名函数,组装成一个新的函数,即新的 dispatch
dispatch = compose(...chain)(store.dispatch)
return {
...store,
dispatch
}
}
}
从applyMiddleware中可以看出∶
- redux中间件接受一个对象作为参数,对象的参数上有两个字段 dispatch 和 getState,分别代表着 Redux Store 上的两个同名函数。
- 柯里化函数两端一个是 middewares,一个是store.dispatch
7. mobx 和 redux 有什么区别?
(1)共同点
- 为了解决状态管理混乱,无法有效同步的问题统一维护管理应用状态
- 某一状态只有一个可信数据来源(通常命名为
store
,指状态容器) - 操作更新状态方式统一,并且可控(通常以
action
方式提供更新状态的途径) - 支持将store与React组件连接,如react-redux,mobx-react
(2)区别
Redux更多的是遵循Flux模式的一种实现,是一个 JavaScript库,它关注点主要是以下几方面∶
Action∶ 一个JavaScript对象,描述动作相关信息,主要包含type属性和payload属性∶
- type∶ action 类型;
- payload∶ 负载数据;
Reducer∶ 定义应用状态如何响应不同动作(action),如何更新状态;
Store∶ 管理action
和reducer
及其关系的对象,主要提供以下功能∶
-
维护应用状态并支持访问状态(
getState()
); -
支持监听action的分发,更新状态(
dispatch(action)
); -
支持订阅store的变更(
subscribe(listener)
);
异步流∶ 由于Redux所有对store状态的变更,都应该通过action触发,异步任务(通常都是业务或获取数据任务)也不例外,而为了不将业务或数据相关的任务混入React组件中,就需要使用其他框架配合管理异步任务流程,如redux-thunk,redux-saga等;
Mobx是一个透明函数响应式编程的状态管理库,它使得状态管理简单可伸缩∶
- Action∶定义改变状态的动作函数,包括如何变更状态;
- Store∶ 集中管理模块状态(State)和动作(action)
- Derivation(衍生)∶ 从应用状态中派生而出,且没有任何其他影响的数据
对比总结:
- redux将数据保存在单一的store中,mobx将数据保存在分散的多个store中
- redux使用plain object保存数据,需要手动处理变化后的操作;mobx适用observable保存数据,数据变化后自动处理响应的操作
- redux使用不可变状态,这意味着状态是只读的,不能直接去修改它,而是应该返回一个新的状态,同时使用纯函数;mobx中的状态是可变的,可以直接对其进行修改
- mobx相对来说比较简单,在其中有很多的抽象,mobx更多的使用面向对象的编程思维;redux会比较复杂,因为其中的函数式编程思想掌握起来不是那么容易,同时需要借助一系列的中间件来处理异步和副作用
- mobx中有更多的抽象和封装,调试会比较困难,同时结果也难以预测;而redux提供能够进行时间回溯的开发工具,同时其纯函数以及更少的抽象,让调试变得更加的容易。
8. Redux 中间件是怎么拿到store 和 action? 然后怎么处理?
redux中间件本质就是一个函数柯里化。
redux applyMiddleware Api 源码中每个middleware 接受2个参数, Store 的getState 函数
和dispatch 函数
,分别获得store和action,最终返回一个函数。
该函数会被传入 next 的下一个 middleware 的 dispatch 方法,并返回一个接收 action 的新函数,这个函数可以直接调用 next(action),或者在其他需要的时刻调用,甚至根本不去调用它。
调用链中最后一个 middleware 会接受真实的 store的 dispatch 方法作为 next 参数,并借此结束调用链。所以,middleware 的函数签名是({ getState,dispatch })=> next => action
。
export default function applyMiddleware(...middlewares) {
return createStore => (...args) => {
// 利用传入的createStore和reducer和创建一个store
const store = createStore(...args)
let dispatch = () => {
throw new Error()
}
const middlewareAPI = {
getState: store.getState,
dispatch: (...args) => dispatch(...args)
}
// 让每个 middleware 带着 middlewareAPI 这个参数分别执行一遍
const chain = middlewares.map(middleware => middleware(middlewareAPI))
// 接着 compose 将 chain 中的所有匿名函数,组装成一个新的函数,即新的 dispatch
dispatch = compose(...chain)(store.dispatch)
return {
...store,
dispatch
}
}
}
9. Redux中的connect有什么作用
connect负责连接React和Redux
- 获取state
connect 通过 context获取 Provider 中的 store,通过store.getState() 获取整个store tree 上所有state
- 包装原组件
将state和action通过props的方式传入到原组件内部 wrapWithConnect 返回—个 ReactComponent 对象 Connect,Connect 重新 render 外部传入的原组件 WrappedComponent ,并把 connect 中传入的 mapStateToProps,mapDispatchToProps与组件上原有的 props合并后,通过属性的方式传给WrappedComponent
- 监听store tree变化
connect缓存了store tree中state的状态,通过当前state状态 和变更前 state 状态进行比较,从而确定是否调用 this.setState()
方法触发Connect及其子组件的重新渲染