浅谈前端状态管理(下)

浅谈前端状态管理(下)

吴双 Clarence吴双 Clarence

上一篇文章中围绕 React 介绍了很多。不可否认的是 React 社区在 Web 应用架构方面的产出的确非常多,不过除了这些产品之外也有一些状态管理的工具是值得一提的。


Vuex

我们业务中使用 Vue 的比例是最高的,说到 Vue 中的状态管理就不得不提到 Vuex。Vuex 也是基于 Flux 思想的产品,所以在某种意义上它和 Redux 很像,但又有不同,下面通过 Vuex 和 Redux 的对比来看看 Vuex 有什么区别。

首先,和 Redux 中使用不可变数据来表示 state 不同,Vuex 中没有 reducer 来生成全新的 state 来替换旧的 state,Vuex 中的 state 是可以被修改的。这么做的原因和 Vue 的运行机制有关系,Vue 基于 ES5 中的 getter/setter 来实现视图和数据的双向绑定,因此 Vuex 中 state 的变更可以通过 setter 通知到视图中对应的指令来实现视图更新。

另外,在 Vuex 中也可以记录每次 state 改变的具体内容,state 的变更可被记录与追踪。例如 Vue 的官方调试工具中就集成了 Vuex 的调试工具,使用起来和 Redux 的调试工具很相似,都可以根据某次变更的 state 记录实现视图快照。

上面说到,Vuex 中的 state 是可修改的,而修改 state 的方式不是通过 actions,而是通过 mutations。一个 mutation 是由一个 type 和与其对应的 handler 构成的,type 是一个字符串类型用以作为 key 去识别具体的某个 mutation,handler 则是对 state 实际进行变更的函数。

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

// mutations
const mutations = {
  [ADD_BOOKS](state, book) {
    state.books.push(book)
  }
}

那么 action 呢?Vuex 中的 action 也是 store 的组成部分,它可以被看成是连接视图与 state 的桥梁,它会被视图调用,并由它来调用 mutation handler,向 mutation 传入 payload。

这时问题来了,Vuex 中为什么要增加 action 这一层呢,是多此一举吗?

当然不是,在知乎上有这样一个问题可以当做很好的栗子:Vue.js中ajax请求代码应该写在组件的methods中还是vuex的actions中?这个问题的答案并不唯一,但通过这个问题可以很好的说明一个 Vuex 的概念——mutation 必须是同步函数,而 action 可以包含任意的异步操作。

回到这个问题本身,如果在视图中不进行异步操作(例如调用后端 API)只是触发 action 的话,异步操作将会在 action 内部执行:

const actions = {
  addBook({ commit }) {
    request.get(BOOK_API).then(res => commit(ADD_BOOK, res.body.new_book))
  }
}

可以看出,这里的状态变更相当于是 action 产生的副作用,mutation 的作用是将这些副作用记录下来,这样就形成了一个完整数据流闭环,数据流的顺序如下:

  1. 在视图中触发 action,并根据实际情况传入需要的参数。

  2. 在 action 中触发所需的 mutation,在 mutation 函数中改变 state。

  3. 通过 getter/setter 实现的双向绑定会自动更新对应的视图。

MobX

MobX 是一个比较新的状态管理库,它的前身是 Mobservable,实际上 MobX 相当于是 Mobservable 的 2.0 版本。它的上升势头很猛,在 React 社区中很受关注,在不久前刚结束的 React Conf 2017 中也有相关的分享(需翻墙):Preethi Kasireddy - MobX vs Redux: Comparing the Opposing Paradigms - React Conf 2017

Mobx 和 Redux 相比,差别就比较大了。如果说 Redux 吸收并发扬了很多函数式编程思想的话,Mobx 则更多体现了面向对象及的特点。MobX 的特点总结起来有以下几点:

  1. Observable。它的 state 是可被观察的,无论是基本数据类型还是引用数据类型,都可以使用 MobX 的 (@)observable 来转变为 observable value。
  2. Reactions。它包含不同的概念,基于被观察数据的更新导致某个计算值(computed values),或者是发送网络请求以及更新视图等,都属于响应的范畴,这也是响应式编程(Reactive Programming)在 JavaScript 中的一个应用。
  3. Actions。它相当于所有响应的源头,例如用户在视图上的操作,或是某个网络请求的响应导致的被观察数据的变更。

和 Redux 对单向数据流的严格规范不同,Mobx 只专注于从 store 到 view 的过程。在 Redux 中,数据的变更需要监听(可见上文 Redux 示例代码),而 Mobx 的数据依赖是基于运行时的,这点和 Vuex 更为接近。它的 store 组织起来大概像这样:

class BookStore {
  books = []
  @observable admin = ''
  @computed get availableBooks() {
    return this.books.filter(book => !book.isAvailable);
  }
}

和 Vuex 一样,比较直观。

而在修改数据方面,Mobx 的操作成本是最低的,它的 store 基于 class 实现,因此可以直接进行修改,不需要像 Vuex 一样触发 mutation 或是和 Redux 一样调用 reducer 并返回新的 state,对开发更友好。

那么 Mobx 是怎么将数据和视图关联起来的呢?我们知道,在 React 中,组件是由无状态函数(stateless function)渲染的,我们只要在组件中加入 mobx-react 这个包提供的 (@)observer 函数(或使用 ES7 decorator 语法),就可以在 store 被改变时自动 re-render 引用了相应数据的 React 组件。

import React, {Component} from 'react'
import ReactDOM from 'react-dom'
import {observer} from 'mobx-react'

@observer
class BookStoreView extends Component {
  render() {
    return (
      <div>
        <ul>
          {this.props.bookStore.books.map(book =>
            <BookView book={book} author={book.author} />
          )}
        </ul>
      </div>
    )
  }
}

const BookView = observer(({book}) =>
  <li>
    <input
      type="checkbox"
      checked={book.isAvailable}
      onClick={() => book.isAvailable = !book.isAvailable}
    />{book.title}
  </li>
)

const store = new BookStore();
ReactDOM.render(<BookStoreView bookStore={store} />, document.getElementById('app'));

可以看到,所有操作数据的方式在组件中直接进行。

虽然 Mobx 提供了便捷的代码书写方式,但这样容易造成 store 被随意修改,在项目规模比较大的时候,像 Vuex 和 Redux 一样对修改数据的入口进行限制可以提高安全性。在 Mobx 2.2 之后的版本中可以通过 useStrict 限制只能通过 action 对数据进行修改。

上文提到,Mobx 只专注于从 store 到 view 的过程,所以业务逻辑的规划没有一定的标准遵循,社区目前也没有很好的最佳实践,需要开发者们在实际开发中积累经验,规划好代码。

总结

前文的评论中有人提到,「 怎么都是在说 React 」其实并不是作者本人偏爱 React,而且实际上在大前端内部 React 技术栈所占比例并不算高。我们对待各个技术栈的态度是不站队,而是以包容的心态学习不同技术的优点。

虽然大家对 React 的看法一度褒贬不一,但不可否认的是 Facebook 的技术团队确实给前端界带来了很大的技术冲击,状态管理这一块的架构思想大多是由 React 社区所引导的,其他社区或多或少都受到了 React 的影响,并在他们的基础上加以改进。前端技术也正是在这样的氛围中不断发展的。

另一方面,状态管理的研究并不是前端领域独有的问题,实际上前端状态管理的很多思想都是借鉴于成熟很多的软件开发体系。相对于软件开发,前端还是一个很新的领域,只有多学习其他领域的优秀经验前端界才能发展得更好。

「真诚赞赏,手留余香」
4 人赞赏
胡小妞
Furybean
塞翁
袁斌
文章被以下专栏收录
7 条评论