如果你选择了其中一个状态管理方案,你不会感到被它锁定了。因为你可以在任何时候切换到另一个解决方案。你可以从 Mobx 换成 Redux 或从 Redux 换成 Mobx。我下面会展示如何能够做到。
Dan Abramov 的 Redux 是从 flux 架构派生出来的。和 flux 不同的是,Redux 用单一 store 而不是多个 store 来保存 state,另外,它用纯函数替代 dispatcher 来修改 state,如果你对 flux 不熟并且没接触过状态管理,不要被这段内容所烦恼。
Redux 被 FP(函数式编程)原则所影响。FP 可以在 JavaScript 中使用,但很多人有面向对象语言的背景,比如 Java。他们在刚开始的时候很难适应函数式编程的原则。这就是为什么对于初学者来说 Mobx 可能更加简单。
既然 Redux 拥抱 FP,那它使用的就是纯函数。一个接受输入并返回输出并且没有其他依赖的纯函数。一个纯函数在相同的输入下输出总是相同而且没有任何副作用。
(state, action) => newState
你的 Redux state 是不可变的,你应该总是返回一个新的 state 而不是修改原 state。你不应该执行 state 的修改或依据对象引用的更改。
// don't do this in Redux, because it mutates the array function addAuthor(state, action) { return state.authors.push(action.author); } // stay immutable and always return a new object function addAuthor(state, action) { return [ ...state.authors, action.author ]; }
最后,在 Redux 的习惯用法里,state 的格式是像数据库一样标准化的。实体之间只靠 id 互相引用,这是最佳实践。虽然不是每个人都这样做,你也可以使用 normalizr 来使 state 标准化。标准化的 state 让你能够保持一个扁平的 state 和保持实体为单一数据源。
{ post: { id: 'a', authorId: 'b', ... }, author: { id: 'b', postIds: ['a', ...], ... } }
Michel Weststrate 的 Mobx 则是受到面向对象编程和响应式编程的影响。它将 state 包装成可观察的对象,因此你的 state 就有了 Observable 的所有能力。state 数据可以只有普通的 setter 和 getter,但 observable 让我们能在数据改变的时候得到更新的值。
Mobx 的 state 是可变的,所以你直接的修改 state :
function addAuthor(author) { this.authors.push(author); }
除此之外,state 实体保持嵌套的数据结构来互相关联。你不必标准化 state,而是让它们保持嵌套。
{ post: { id: 'a', ... author: { id: 'b', ... } } }
单 store 与多 stores
在 Redux 中,你将所有的 state 都放在一个全局的 store。这个 store 对象就是你的单一数据源。另一方面,多个 reducers 允许你修改不可变的 state。
Mobx 则相反,它使用多 stores。和 Redux 的 reducers 类似,你可以在技术层面或领域进行分治。也许你想在不同的 stores 里保存你的领域实体,但仍然保持对视图中 state 的控制。毕竟你配置 state 是为了让应用看起来更合理。
从技术层面来说,你一样可以在 Redux 中使用多个 stores。没有人强迫你只能只用一个 store。 但那不是 Redux 建议的用法。因为那违反了最佳实践。在 Redux 中,你的单 store 通过 reducers 的全局事件来响应更新。
如何使用?
你需要跟随下面的代码学习使用 Redux,首先在全局 state 上新增一个 user 数组。你可以看到我通过对象扩展运算符来返回一个新对象。你同样可以在 ES6(原文为 ES5,实际是应该是 ES6)中使用 Object.assign() 来操作不可变对象。
const initialState = { users: [ { name: 'Dan' }, { name: 'Michel' } ] }; // reducer function users(state = initialState, action) { switch (action.type) { case 'USER_ADD': return { ...state, users: [ ...state.users, action.user ] }; default: return state; } } // action { type: 'USER_ADD', user: user };
你必须使用 dispatch({ type: 'USER_ADD', user: user });来为全局 state 添加一个新 user 。
在 Mobx 中,一个 store 只管理一个子 state(就像 Redux 中管理子 state 的 reducer),但你可以直接修改 state 。
@observable 让我们可以观察到 state 的变化。
class UserStore { @observable users = [ { name: 'Dan' }, { name: 'Michel' } ]; }
现在我们就可以调用 store 实例的方法:userStore.users.push(user);。这是一种最佳实践,虽然使用 actions 去操作 state 的修改更加清楚明确。
class UserStore { @observable users = [ { name: 'Dan' }, { name: 'Michel' } ]; @action addUser = (user) => { this.users.push(user); } }
在 Mobx 中你可以加上 useStrict() 来强制使用 action。现在你可以调用 store 实例上的方法:userStore.addUser(user); 来修改你的 state 。