Redux及其面试题

### Redux

### 三项基本原则

1. 单一数据源:所有状态都保存在单一的store中

2. state是只读的:不能直接对state进行修改,只能用新的state替换旧的state

3. Reducer必须是一个纯函数

   > 纯函数指的是,给固定的输入,就一定会有固定的输出,而且不会有任何副作用

**Redux使用场景**

* 某个组件的状态,需要共享
* 某个状态需要在任何地方都可以拿到
* 在一个组件中需要改变全局状态
* 在一个组件中需要改变另一个组件的状态

例子

```js
import { createStore } from 'redux'
// 自定义其形参stata的默认值
const initState = {
    currentUser:{},
    isLogin=false,
}
// 第二步:定义reducer(作用是初始化数据并指定修改方法)
const reducer = function (state = initState, action) {
    // 形参state:状态(共享的数据),一般会设定默认值
    // 形参action:指定数据的修改方式,一般是一个对象:{type:'',payload}
    switch (action.type) {
        case 'login':
                // 每次返回的都是一个全新的state对象
            return {
                ...state,
                currentUser: action.user,
                isLogin: true,
            };
        // store.dispatch({type:'logout'})
        case 'logout':
            return initState;
        default:
            return state;
    }
}

// 第一步,创建store;redux.createStore,需要传入reducer
const store = createStore(reducer);

export default store

/*
        在组件件就可以引入store并使用其方法
    * store.getState() 获取仓库最新状态
    * store.dispatch(action) 修改数据唯一方式,会触发reducer函数
    * store.subscribe(fn) 监听数据修改,fn是一个函数;state数据更新,会触发
*/
```

1. 数据存储仓库:Store

   > store是Redux存储区(一个保存数据的地方),你可以把它看成一个仓库,**规定一个应用只能有一个Store**,store中的数据可以被直接访问,但只能通过提供的reducer进行更新。
   >
   > ```js
   >     import { createStore } from 'redux';
   > 
   >     const reducer = function (state = initState, action) {
   >                    return state
   >     }
   >     const store = createStore(reducer);
   > 
   >  export default store;
   > ```
   >
   
2. 状态初始化与更新逻辑:reducer函数

   > Reducer 必须是一个**纯函数**,用于指定state修改逻辑,**它接受当前 state 和 action 作为参数,并根据state和action的值返回新的state**
   >
   > + 第一个参数是state
   > + 第二个参数是action, `{type,payload}`,此action就是store.dispatch(action)里的action
   >
   > ```js
   > const initState = {
   >     currentUser:{},
   >     isLogin=false,
   > }
   > // 第二步:定义reducer(作用是初始化数据并指定修改方法)
   > const reducer = function (state = initState, action) {
   >     // 形参state:状态(共享的数据),一般会设定默认值
   >     // 形参action:指定数据的修改方式,一般是一个对象:{type:'',payload}
   >     switch (action.type) {
   >         case 'login':
   >                 // 每次返回的都是一个全新的state对象
   >             return {
   >                 ...state,
   >                 currentUser: action.user,
   >                 isLogin: true,
   >             };
   >         // store.dispatch({type:'logout'})
   >         case 'logout':
   >             return initState;
   >         default:
   >             return state;
   >     }
   > }
   > ```

3. 在组件中获取state,获取仓库最新状态

   ```js
    store.getState();
   ```

4. 在组件中修改数据,修改数据唯一方式。调用dispatch()方法,其内部会调用reducer()方法

   ```js
    store.dispatch({type:'login',user:lzp});
   ```

5. store.subscribe(fn) 监听数据修改;fn是一个函数;state数据更新,会触发

   ```react
       componentDidMount(){
           store.subscribe(()=>{
               console.log('statechange=',store.getState())
                 // 为了实现刷新而另组件state改变
               // this.setState({
               //     isLogin:store.getState().isLogin
               // })
           });
       }
   ```

   在react中使用redux的原生:在react里面我们可以在componentDidMount生命周期里定义store.subscribe(fn);在fn里面可以使用store.getState()获取数据,再让setState()修改当前组件state实现**刷新**;非常麻烦

### react-redux 桥接工具

为了方便使用,Redux 的作者封装了一个 React 专用的库 React-Redux,它提供了一些接口,用于Redux的状态和React的组件展示结合起来,以用于实现状态与视图的一一对应

+ HOC 与 context 通信在 react-redux 底层中的原理

  > `<Provider/>`:利用**Context**共享redux的数据
  > `connect()`:使用**高阶组件**把数据作为props传入React组件

#### `<Provider/>` 容器组件

* React-Redux 提供Provider组件,接受Redux的store作为props
* 可结合`connect`方法可以让容器组件方便获取state和dispatch

```react
// react-redux第一步:利用Provider共享store
import { Provider } from 'react-redux';
import store from '@/store';

ReactDOM.render(
    <Provider store={store}>
        <App/>
    </Provider>,
    document.getElementById('app')
)
```

#### `connect()`高阶组件

connect( )方法为React-Redux核心方法,它把react组件与redux中的Store真正连接在一起,connect方法接受两个参数(Function类型),用于定义了 UI 组件的业务逻辑,返回一个高阶组件

* 格式:`connect([mapStateToProps],[mapDispatchToProps])(某组件)`

  > * 第一个参数:mapStateToProps
  >   * 负责组件的输入逻辑,即将**state**映射到 UI 组件的参数(props)
  >   * 必须返回一个对象
  >
  > * 第二个参数:mapDispatchToProps
  >   * 负责组件的输出逻辑,即将用户对 UI 组件的操作映射成 Action
  >   * 必须返回一个对象
  >   * 不写mapDispatchToProps参数,默认在props里添加了dispatch方法
  >
  > ```react
  > import { connect } from 'react-redux'
  > function mapStateToProps(state,ownprops) {
  >     //state:redux中的state
  >     //ownprops: Cart组件自身的props
  >     return {
  >       //将state中购物车页面的goodsinf数据映射到props,Cart组件中通过props.data访问
  >       data: state.shoppingCart.goodsinf
  >     }
  > }
  > function mapDispatchToProps(dispatch,ownprops) {
  >     // dispatch: redux中的dispatch方法
  >     // ownprops:Cart组件自身的props
  >     return {
  >       dispatch,//书写了mapDispatchToProps,需要手动添加回dispatch
  >       logout() {
  >             // 自定义dispatch方法,Cart组件中通过props.logout访问
  >           dispatch({ type: 'logout' })
  >       }
  >     }
  > }
  > // 类组件可使用装饰器
  > // @connect(mapStateToProps,mapDispatchToProps)
  > 
  > // 调用connect(),其返回值是高阶组件
  > // 当前组件包装了connect(),不写第二个参数默认在组件props里添加了dispatch方法;
  > // 当connect(a,b)添加了第二个参数;需要手动添加dispatch方法;
  > Cart = connect(mapStateToProps,mapDispatchToProps)(Cart);
  > export default App
  > ```
  >
  

### combineReducers实现redux的模块化

复杂的应用需要使用多个Reducer来实现业务逻辑,把reducer设置成单独模块,并使用**combineReducers**合并成一个大的Reducer后使用

```js
    import { createStore,combineReducers } from "redux";

    import productsReducer from './productsReducer';
    import cartReducer from './cartReducer';
    //合并Reducer
    const allReducers = {
        products: productsReducer,
        cart: cartReducer
    }
    const rootReducer = combineReducers(allReducers);

    // 合并模块化之后,每个模块里面的数据,获取的时候会多出了一层模块名
    // 例如:props.state.goodsinf >>> props.state.cart.goodsinf
    const store = createStore(rootReducer);

        export default store;
```

### redux简单版createStore

```js
// 手动实现一个简单版的createStore封装
// 当你不会封装函数时,先调用。查看返回值。
// * store.getState() 获取仓库最新状态
// * store.dispatch(action) 修改数据唯一方式
// * store.subscribe(fn) 监听数据修改,fn是一个函数,因为state数据更新,会触发
export function createStore() {
    let state = reducer();
    const listener = []
    // 获取state
    getState = function () {
        return state;
    }
    // 设置state
    dispatch = function (action) {
        // 获取新的state
        state = reducer(state, action)
        // 执行监听state的函数
        for (let i = 0; i < listener.length; i++) {
            typeof (listener[i]) === 'function' && listener[i]()
        }
    }
    // 监听state 
    subscribe = function (fn) {
        // fn的代码在修改state时触发,放到设置state函数体内
        // 可能多个地方使用到监听,保存至数组中
        listener.push(fn);
        // 监听分返回值是取消监听
        return function unsbuscribe() {
            const idx = listener.indexOf(fn);
            // 将其从监听数组中删除即可
            listener.splice(idx, 1)
        }
    }
    // 修改reducer
    replaceReducer = function (newReducer) {
        reducer = newReducer;
    }
    return {
        getState,
        dispatch,
        subscribe,
        replaceReducer
    }
}
```

### bindActionCreators

利用redux的`bindActionCreators`把`Action Creator`中默认导出的所有方法(export default中的方法)绑定到组件props并自动**隐式调用dispatch(action)**

1. 在./userActions.js文件定义Action Creator

   ```js
   /**
    * Action Creator 定义在 ./userActions.js
    */
   function login(user){
        return {
            type:'login',
            user
        }
    }
   function logout(){
       return {
           type:'logout'
       }
   }
    export default {
        login,
        logout
    }
   ```

2. bindActionCreators默认导出的所有方法(export default中的方法)绑定到组件props,并自动隐式调用dispatch(action)

   ```js
   import { bindActionCreators } from 'redux';
   import { connect } from 'react-redux';
   import userActions from './userActions';//自定义封装的Action Creator
   //...
   const mapDispatchToProps = dispatch =>{
           /*  return {
           dispatch,
           login(user){
               dispatch(userAction.login(user))
           },
           logout(){
               dispatch(userAction.logout())
           }
       } */
         // bindActionCreators默认导出的所有方法(export default中的方法)绑定到组件props
       // 并自动隐式调用dispatch(action),如上面代码
       return bindActionCreators(userAction,dispatch)
   }
   
   MyComponent = connect(state=>state, mapDispatchToProps)(MyComponent)
   export default MyComponent;
   ```

### Generator与Iterator

+ 生成器函数Generator

  > 1. 执行生成器函数并不会执行函数中的代码,而是返回一个**迭代器**
  >
  > 2. 定义函数时添加星号: *
  >
  > 3. 在生成器函数中,yield暂停并返回,可以有多个;return结束并返回,只有一个
  >
  >    ```js
  >    function * create(){
  >        console.log('start')//调用 create()并不会执行。
  >        yield 10;
  >        console.log(1)
  >        yield 50;   // 暂停并返回
  >        console.log(2)
  >        return 100; //结束并返回
  >        console.log(3)
  >    }
  >    p = create()
  >    p.next()//输出start,返回值{value: 10, done: false} 
  >    p.next()//输出1,返回值{value: 50, done: false}
  >    p.next()//输出2,返回值{value: 100, done: true}
  >    p.next()//代码不会执行了,直接返回 {value: undefined, done: true}
  >    ```

+ 迭代器Iterator

  > 内部包含多个值的对象,要获取迭代器中的值,必须调用`next()`方法,没调用一次next就是一次迭代,next方法的返回值格式:{ value,done }
  >
  > + value:每一次迭代返回的值
  > + done: 标识迭代器是否迭代完成
  > + <suspended> 没有执行完,<closed> 执行完

**数组、字符串其内置身上有symbol.iterator()方法;可用for...of遍历具有迭代器的数据(不断执行迭代器的next()方法,直到迭代完成)**

### redux-saga 中间件 

**对 dispatch 改装,实现 redux 异步处理中间件**
redux中的action仅支持原始对象,处理有副作用的action(异步请求),可以使用中间件。**中间件可以在发出action,到reducer函数接受action之间,执行具有副作用的操作**。

+ saga原理:生成器和迭代器配合
+ saga中间件会在适当的时候自动调用迭代器的next()方法
+ 使用中间件需要用到redux的**applyMiddleware**方法接受

```js
    import {createStore,applyMiddleware} from 'redux';
    // 1.引入saga
    import createSagaMiddleware from 'redux-saga';
    // 自定义的rootSaga文件和reducer文件
    import rootSaga from './middleware/rootSaga.js';
    import reducer from './reducers'

    // 2.创建saga中间件
    const sagaMiddleware = createSagaMiddleware();

    // 3.将saga中间件sagaMiddleware 连接至 Store
    const store  = createStore(reducer,applyMiddleware(sagaMiddleware));

    // 4.saga中间件引入并运行自定义Saga配置
    sagaMiddleware.run(rootSaga);
```

rootSaga.js文件里自定义saga action

+ takeEvery 是普通执行异步action的方法
+ takeLatest也是执行异步action的方法,但是其内部做了防抖处理(最后一次生效)
+ put 就是 dispatch
+ call、apply等效于原生js中的call、apply,主要是为了方便测试(单元测试,点对点测试)

```js
import request from '@/utils/request'//封装的axios请求
import { call, apply, put, takeEvery, takeLatest } from 'redux-saga/effects';

//  自定义生成器函数管理saga action,中间件会在适当的时候自动调用迭代器的next()方法
function* initial() {
    // 监听异步action:saga action
    yield takeEvery('login_async',login);   // 可同时执行多个action
    // yield takeLatest('login_async', login); // takeLatest做了防抖优化,只生效最后一次
    // ···全部的saga action定义在这里
}

function* login(sagaAction) {
    // 在组件里调用dispatch({type:'login_async',data:{username,password,remember}})
    // 形参sagaAction就接收dispatch传递的整个action对象:{type: "login_async", data: {…}} */
    const { data } = yield request.get('/user/login', {
        params: sagaAction.data
    })
    
    // call方法实现
    // const {data} = yield call(request.get,'/user/login',{    
    //     params:sagaAction.data
    // })
        
    // 等待异步请求结果返回后自动执行next()方法,调用同步reducer action
    const action = { type: 'login', user: data.data }
    yield put(action)// 这里 put 就是 dispatch
}
export default initial;
```

* 调用

  > 即使对dispatch进行了改装,调用方式还是那个;
  > this.props.dispatch({ type: 'login_async', data: { username, password } })

* 实现流程

  > 1. 组件里发起异步action:dispatch({type:'login_async',data:{username,password}})
  > 2. 生成器监听器匹配到异步action,通过**takeEvery()**或**takeLatest()**发送异步请求方法。
  > 3. 待请求响应数据回来,会自动调用**next()**方法,来执行**put()**方法或者**dispatch()**方法

# 面试题

## 说说你对Redux的理解?其工作原理?

**Redux是什么?**

`React`是用于构建用户界面的,帮助我们解决渲染`DOM`的过程

而在整个应用中会存在很多个组件,每个组件的`state`是由自身进行管理,包括组件定义自身的`state`、组件之间的通信通过`props`传递、使用`Context`实现数据共享

如果让每个组件都存储自身相关的状态,理论上来讲不会影响应用的运行,但在开发及后续维护阶段,我们将花费大量精力去查询状态的变化过程

这种情况下,如果**将所有的状态进行集中管理,当需要更新状态的时候,仅需要对这个管理集中处理,而不用去关心状态是如何分发到每一个组件内部的**

`redux`就是一个实现上述集中管理的容器,遵循三大基本原则:

- **单一数据源**
- **state 是只读的**
- **使用纯函数来执行修改**

注意的是,`redux`并不是只应用在`react`中,还与其他界面库一起使用,如`Vue`

**工作原理**

`redux `要求我们把数据都放在 `store `公共存储空间

一个组件改变了 `store` 里的数据内容,其他组件就能感知到 `store `的变化,再来取数据,从而间接的实现了这些数据传递的功能

工作流程图如下所示:

![](https://static.vue-js.com/27b2e930-e56b-11eb-85f6-6fac77c0c9b3.png)

**如何使用**

创建一个`store`的公共数据区域

```js
import { createStore } from 'redux' // 引入一个第三方的方法
const store = createStore() // 创建数据的公共存储区域(管理员)
```

还需要创建一个记录本去辅助管理数据,也就是`reduecer`,本质就是一个函数,接收两个参数`state`,`action`,返回`state`

```js
// 设置默认值
const initialState = {
  counter: 0
}

const reducer = (state = initialState, action) => {
}
```

然后就可以将记录本传递给`store`,两者建立连接。如下:

```js
const store = createStore(reducer)
```

如果想要获取`store`里面的数据,则通过`store.getState()`来获取当前`state`

```js
console.log(store.getState());
```

下面再看看如何更改`store`里面数据,是通过`dispatch`来派发`action`,通常`action`中都会有`type`属性,也可以携带其他的数据

```js
store.dispatch({
  type: "INCREMENT"
})

store.dispath({
  type: "DECREMENT"
})

store.dispatch({
  type: "ADD_NUMBER",
  number: 5
})
```

下面再来看看修改`reducer`中的处理逻辑:

```js
const reducer = (state = initialState, action) => {
  switch (action.type) {
    case "INCREMENT":
      return {...state, counter: state.counter + 1};
    case "DECREMENT":
      return {...state, counter: state.counter - 1};
    case "ADD_NUMBER":
      return {...state, counter: state.counter + action.number}
    default: 
      return state;
  }
}
```

注意,`reducer`是一个纯函数,不需要直接修改`state`

这样派发`action`之后,既可以通过`store.subscribe`监听`store`的变化,如下:

```js
store.subscribe(() => {
  console.log(store.getState());
})
```

在`React`项目中,会搭配`react-redux`进行使用

完整代码如下:

```js
const redux = require('redux');

const initialState = {
  counter: 0
}

// 创建reducer
const reducer = (state = initialState, action) => {
  switch (action.type) {
    case "INCREMENT":
      return {...state, counter: state.counter + 1};
    case "DECREMENT":
      return {...state, counter: state.counter - 1};
    case "ADD_NUMBER":
      return {...state, counter: state.counter + action.number}
    default: 
      return state;
  }
}

// 根据reducer创建store
const store = redux.createStore(reducer);

store.subscribe(() => {
  console.log(store.getState());
})

// 修改store中的state
store.dispatch({
  type: "INCREMENT"
})
// console.log(store.getState());

store.dispatch({
  type: "DECREMENT"
})
// console.log(store.getState());

store.dispatch({
  type: "ADD_NUMBER",
  number: 5
})
// console.log(store.getState());
```

## 说说对Redux中间件的理解?常用的中间件有哪些?实现原理?

**中间件(Middleware)**是介于应用系统和系统软件之间的一类软件,它使用系统软件所提供的基础服务(功能),衔接网络上应用系统的各个部分或不同的应用,能够达到资源共享、功能共享的目的。

了解到了`Redux`整个工作流程,当`action`发出之后,`reducer`立即算出`state`,整个过程是一个同步的操作;那么如果需要支持异步操作,或者支持错误处理、日志监控,这个过程就可以用上中间件

`Redux`中,中间件就是放在就是在`dispatch`过程,在分发`action`进行拦截处理,其本质上一个函数,对**`store.dispatch`方法进行了改造,在发出 `Action `和执行 `Reducer `这两步之间,添加了其他功能**

**常用的中间件**

有很多优秀的`redux`中间件,如:

- redux-thunk:用于异步操作
- redux-saga:用于异步操作
- redux-logger:用于日志记录

上述的中间件都需要通过`applyMiddlewares`进行注册,作用是将所有的中间件组成一个数组,依次执行

然后作为第二个参数传入到`createStore`中

```js
import {createStore,applyMiddleware} from 'redux';
const store = createStore(
  reducer,
  applyMiddleware(thunk, logger)
);
```

## 你在React项目中是如何使用Redux的? 项目结构是如何划分的?

`redux`是用于数据状态管理,而`react`是一个视图层面的库;如果将两者连接在一起,可以使用官方推荐`react-redux`库,其具有高效且灵活的特性

使用`react-redux`分成了两大核心:

- Provider
- connection

**Provider**

在`redux`中存在一个`store`用于存储`state`,如果将这个`store`存放在顶层元素中,其他组件都被包裹在顶层元素之上

那么所有的组件都能够受到`redux`的控制,都能够获取到`redux`中的数据

使用方式如下:

```react
<Provider store = {store}>
    <App />
<Provider>
```

**connection**

connect`方法将`store`上的`getState `和 `dispatch `包装成组件的`props

可以传递两个参数:

- mapStateToProps
- mapDispatchToProps

```js
import { connect } from "react-redux";

connect(mapStateToProps, mapDispatchToProps)(MyComponent)
```

**mapStateToProps**

把`redux`中的数据映射到`react`中的`props`中去,组件内部就能够通过`props`获取到`store`中的数据

```react
class Foo extends Component {
    constructor(props){
        super(props);
    }
    render(){
        return(
         // 这样子渲染的其实就是state.bar的数据了
            <div>this.props.foo</div>
        )
    }
}

const mapStateToProps = (state) => {
    return {
        // 意思是将state中的某个数据映射到props中
        foo: state.bar
    }
}

Foo = connect(mapStateToProps)(Foo)
export default Foo
```

**mapDispatchToProps**

将`redux`中的`dispatch`映射到组件内部的`props`中

```react
class Foo extends Component {
    constructor(props){
        super(props);
    }
    render(){
        return(
         
             <button onClick = {this.props.onClick}>点击increase</button>
        )
    }
}

const mapDispatchToProps = (dispatch) => { 
  // 默认传递参数就是dispatch
  return {
    onClick: () => {
      dispatch({
        type: 'increatment'
      });
    }
  };
}
Foo = connect(state=>state,mapDispatchToProps)(Foo);
export default Foo;
```

------

## Redux中的connect有什么作用?

connect负责连接React和Redux

1. 获取state

   > connect 通过 **context获取 Provider 中的 store**,通过 store.getState() 获取整个store tree 上所有state

2. 包装原组件

   > 将state和action通过props的方式传入到原组件内部 `wrapWithConnect` 返回—个 `ReactComponent` 对象 Connect,Connect重新 render 外部传入的原组件 `WrappedComponent` ,并把 connect 中**传入的 `mapStateToProps`,`mapDispatchToProps`与组件上原有的 props 合并**后,通过属性的方式传给 `WrappedComponent`

3. 监听store tree变化

   > connect缓存了`store tree`中state的状态,通过当前state状态 和变更前 state 状态进行比较,从而**确定是否调用 `this.setState()`方法触发 Connect 及其子组件的重新渲染**

------

## Redux 中异步的请求怎么处理

一般的异步请求,可以在 `componentDidmount` 中直接进⾏请求,⽆须借助redux。

但是在⼀定规模的项⽬中,上述⽅法很难进⾏异步流的管理,通常情况下我们会借助redux的异步中间件进⾏异步处理。

redux异步流中间件其实有很多,当下主流的异步中间件有两种`redux-thunk`、`redux-saga`。

**redux-thunk优点:**

- 体积⼩: redux-thunk的实现⽅式很简单,只有不到20⾏代码
- 使⽤简单: redux-thunk没有引⼊像`redux-saga`或者`redux-observable`额外的范式,上⼿简单

**redux-thunk缺陷:**

- 样板代码过多: 与redux本身⼀样,通常⼀个请求需要⼤量的代码,⽽且很多都是重复性质的
- 耦合严重: 异步操作与redux的action偶合在⼀起,不⽅便管理
- 功能孱弱: 有⼀些实际开发中常⽤的功能需要⾃⼰进⾏封装

**redux-saga优点:**

- 异步解耦: 异步操作被被转移到单独 saga.js 中,不再是掺杂在 action.js 或 component.js 中
- action摆脱`thunk function`: dispatch 的参数依然是⼀个纯粹的 action (FSA),⽽不是充满 “⿊魔法” thunk function
- 异常处理: 受益于 `generator function` 的 saga 实现,代码异常/请求失败 都可以直接通过 `try/catch` 语法直接捕获处理
- 功能强⼤: `redux-saga`提供了⼤量的 Saga 辅助函数和 Effect 创建器供开发者使⽤,开发者⽆须封装或者简单封装即可使⽤
- 灵活: redux-saga可以将多个Saga可以串⾏/并⾏组合起来,形成⼀个⾮常实⽤的异步flow
- 易测试,提供了各种case的测试⽅案,包括mock task,分⽀覆盖等等

**redux-saga缺陷:**

- 额外的学习成本: `redux-saga`不仅在使⽤难以理解的 `generator function`,⽽且有数⼗个API,学习成本远超redux-thunk。最重要的是你的额外学习成本是只服务于这个库的,与`redux-observable`不同,`redux-observable`虽然也有额外学习成本但是背后是rxjs和⼀整套思想
- 体积庞⼤: 体积略⼤,代码近2000⾏,min版25KB左右
- 功能过剩: 实际上并发控制等功能很难⽤到,但是我们依然需要引⼊这些代码
- ts⽀持不友好: yield⽆法返回TS类型

`redux-saga`可以捕获action,然后执行一个函数,那么可以把异步代码放在这个函数中。

## mobx 和 redux 有什么区别?

**共同点**

- 为了解决状态管理混乱、无法有效同步的问题,统一维护管理应用状态
- 某一状态只有一个可信数据来源(通常命名为store,指状态容器)
- 操作更新状态方式统一,并且可控(通常以action方式提供更新状态的途径)
- 支持将store与React组件连接,如`react-redux`,`mobx-react`

**区别**

**Redux**更多的是遵循Flux模式的一种实现,是一个 JavaScript 库,它关注点主要是以下几方面∶

- Action∶ 一个JavaScript对象,描述动作相关信息,主要包含type属性和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提供能够进行时间回溯的开发工具,同时其纯函数以及更少的抽象,让调试变得更加的容易

## Redux 和 Vuex区别?

**相同点**

- state 共享数据
- 流程一致:定义全局state,触发,修改state
- 原理相似,通过全局注入store。

**不同点**

- 从实现原理上来说:
  - Redux 使用的是不可变数据,而Vuex的数据是可变的。Redux每次都是用新的state替换旧的state,而Vuex是直接修改
  - Redux 在检测数据变化的时候,是通过 diff 的方式比较差异的,而Vuex其实和Vue的原理一样,是通过 getter/setter来比较的
- 从表现层来说:
  - vuex定义了state、getter、mutation、action四个对象;redux定义了state、reducer、action。
  - vuex中state统一存放,方便理解;reduxstate依赖所有reducer的初始值
  - vuex有getter,目的是快捷得到state;redux没有这层,react-redux mapStateToProps参数做了这个工作。
  - vuex中mutation只是单纯赋值(很浅的一层);redux中reducer只是单纯设置新state(很浅的一层)。他俩作用类似,但书写方式不同
  - vuex中action有较为复杂的异步ajax请求;redux中action中可简单可复杂,简单就直接发送数据对象({type:xxx, your-data}),复杂需要调用异步ajax(依赖redux-thunk插件)。
  - vuex触发方式有两种commit同步和dispatch异步;redux同步和异步都使用dispatch

通俗点理解就是,vuex 弱化 dispatch,通过commit进行 store状态的一次更变;取消了action概念,不必传入特定的 action形式进行指定变更;弱化reducer,基于commit参数直接对数据进行转变,使得框架更加简易;

**共同思想**

- 单一的数据源
- 变化可以预测

本质上∶ redux与vuex都是对mvvm思想的服务,将数据从视图中抽离的一种方案。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值