探一探前端的状态管理

有关状态管理这个词儿已经出现好几年了。不过实际中大多数项目其实还用不上状态管理,并且即便在项目中用上,但也用得并很差,尤为是初学者,更多的是去记录各类API的用法,殊不知其核心思想,很难快速实现状态管理的最佳实践。javascript

什么是“状态管理”

以前在 Vue组件通讯事件总结 这篇文章里研究过Vue里面各个组件之间的数据通讯,通常页面不是特别复杂的项目,使用这篇文章总结的通讯方式足够了,但当前端页面复杂度愈来愈高,props$emit$listenersref / refseventBus这些方法也许还不够方便,这个时候能够开始尝试vuex的状态管理机制了。前端

用一个通俗易懂的概念来解释状态管理:全局变量。vue

可是在项目的开发中,咱们应该都记得前辈的叮嘱:尽可能不要或少用全局变量,由于那是不可控的操做,任意的操做均可能致使数据的改变,形成全局污染,没法追踪数据变化过程,因此开发中咱们小菜鸟也大都避免去过多的操做全局变量。java

但当项目复杂度比较高时,也许多个组件、多个页面之间须要实现一种数据或状态的共享,这时候,咱们就能够将这些状态统一的进行管理,既能够实现共享状态,又能追踪到数据的变化,可能这也是状态管理诞生的一种缘由吧。vuex

固然,跟全局变量同样,各个文章里面也是叮而嘱之,若是没有哦必要,尽可能不要使用状态管理。编程

状态管理应用包含三部分:
  • state,驱动应用的数据源;
  • view,以声明方式将 state 映射到视图;
  • actions,响应在 view 上的用户输入致使的状态变化。

Flux 架构思想

关于如何在UI试图中合理地修改state,Facebook提出了flux思想。redux

Flux 是一种架构思想,专门解决软件的结构问题。后来的rudux和vux也都是由Flux的思想演化出来的。数据结构

Flux将一个应用分红四个部分: View(视图)、Action(动做)、Dispatcher(派发)、Store(数据)架构

总体流程:框架

这是一个单向流动的过程,

用户访问view -> 触发事件action -> Dispatcher知道后告知store要更新 -> store更新,触发视图View更新页面

特色:

  • 单向数据流
  • Store 能够有多个
  • Store 不只存放数据,还封装了处理数据的方法(便可以直接修改数据状态)

Redux

redux的核心概念:

Redux store 是单一数据源,且store是不可变的,Redux 没有 dispatcher 的概念

特色:

  • 单向数据流
  • 单一数据源,只有一个store
  • state只读
  • 没有 Dispatcher ,而是在 Store 中集成了 dispatch 方法,store.dispatch() 是 View 发出 Action 的惟一途径

Store 中提供了几个管理 state 的 API:

  • store.getState():获取当前 state
  • store.dispatch(action):触发 state 改变(惟一途径)
  • store.subscribe(listener):设置 state 变化的监听函数(若把视图更新函数做为 listener 传入,则可触发视图自动渲染)

总体流程:

Action Creator -> 触发action -> store.dispatch(action) -> reducer(state, action) -> 更新state

MobX

MobX 是经过透明的函数响应式编程使得状态管理变得简单和可扩展,是一个用法简单优雅、同时具备可扩展性的状态管理库。

和 Redux 对单向数据流的严格规范不一样,Mobx 只专一于从 store 到 view 的过程。在 Redux 中,数据的变动须要监听,而 Mobx 的数据依赖是基于运行时的,这点和 Vuex 更为接近。

若是你们使用过 Vue 的话相信对其双向绑定 MVVM 的思想并不陌生,React + Mobx 至关因而 Vue 全局做用域下的双向绑定,而 Vue 的状态管理框架 Vuex 倒是借鉴了 Flux 架构,连尤大都说,彷佛有点你中有我,我中有你的关系。

特色:

  • 每每是多个 Store
  • 数据流流动不天然,只有用到的数据才会引起绑定,局部精确更新
  • 通常适合应用于中小型项目中

Vuex

仍然放一张vuex的示意图:

vuex的运做原理跟redux稍有不一样。由于Vue虽然是单向数据流,但Vue 基于 ES5 中的 getter/setter 来实现视图和数据的双向绑定,所以 Vuex 中 state 的变动能够经过 setter 通知到视图中对应的指令来实现视图更新。且state不是经过actions来修改的,而是经过mutations。

特色:

  • 单向数据流, 经过 store.dispatch() 调用 Action ,在 Action 执行完异步操做以后经过 store.commit() 调用 Mutation 更新 State

  • 单一数据源

  • actions 能够是 异步操做,故可在action中调用后台接口获取新的数据;

  • mutations 只能是 同步操做;

  • mutations 和 actions 均可直接更改 state,可是当 action 含有异步操做时,会使得数据变化混乱,难以跟踪,使得调试困难;基于以上缘由,Vuex 规定只能是 mutations 来改变 state。

总体流程:

用户访问view -> 触发事件action ->action中触发对应的mutation -> 在mutation函数中改变state -> 经过 getter/setter 实现的双向绑定的机制,视图View会自动更新页面

使用一句话总结:commit mutation,dispatch action

几种方案的异同

从上面几种热门的状态管理实现方式来看,能够发现,这几种数据流模型几乎都是从 action 到 view 之间的一种数据流动,总的过程能够大体简化为:

action -> 更新 state(视图层)

一、 数据状态中心: state和store

能够发现,在redux和flux中,数据状态用store表示,而在vuex和mobx中,这个store被state替代。

和 Redux 中使用不可变数据来表示 state 不一样,Vuex 中没有 reducer 来生成全新的 state 来替换旧的 state,Vuex 中的 state 是能够被修改的

Vuex 中的 state 是可修改的,而修改 state 的方式不是经过 actions,而是经过 mutations。更改 Vuex 的 store 中的状态的惟一方法是提交 mutation。

二、 行为action

咱们想要更新视图内容,第一步就是要有触发的动做,action就是向store发起更新请求的最小单元。但action并不是是一个动词,它是一个行为的描述,本质上是一个纯声明式的数据结构,仅提供对事件的描述,不提供事件的具体逻辑

在rudux中,生成action有两种方式:

  • 声明对象:

    const action = {
      type: 'ADD_TODO',
      text: '生成一个action'
    }
    dispatch(action)
    复制代码
  • 函数生成:

    function addTodo(text) {
      return {
        type: ADD_TODO,
        text
      }
    }
    dispatch(addTodo())
    复制代码

三、dispatch

在各种应用状态管理的模型中,一般都会有一个dispatch方法,它就声明在Store上,负责调用各个Action,而后由Store上对应的分发机制进行处理。

四、Reducer / Mutation

如今到了咱们的第三步,更新状态。flux和mobx对状态更新的处理是直接更改,而redux和vuex中间还会多出一个reducer和mutation来处理更新的工做。

至于reducer和mutation,分别是针对redux和vuex的不一样工做机制。

先看reducer,Actions 只能描述发生了什么,并不能描述状态发生了什么变化,Reducers 指定 state tree 将要发生什么,它主要的工做内容是:接受一个action,而后经过switch匹配action的type,做出相应的处理后,返回一个新的对象。为何是新的对象呢?由于redux最核心的一个概念,store是不可变的, Redux 是一个实践函数式编程(FP)理念的库。下面是一个最简单的reducer:

const items = (state = [], action) => {
  switch (action.type) {
    case "ADD_ITEM":
      return [...state, { text: action.text }]
    default:
      return state
  }
}
复制代码

再来看mutation,一个 mutation 是由一个 type 和与其对应的 handler 构成的,type 是一个字符串类型用以做为 key 去识别具体的某个 mutation,handler 则是对 state 实际进行变动的函数。

// store
const store = {
  books: []
}

// mutations
const mutations = {
  [ADD_BOOKS](state, book) {
    state.books.push(book)
  }
}
复制代码
相关文章
相关标签/搜索