sorrycc / blog

💡
4.48k stars 324 forks source link

MobX 入门教程 #2

Open sorrycc opened 8 years ago

sorrycc commented 8 years ago

大家用 redux 这么久,有没有被那么多概念和约定烦到?

比如:

如果有,那么可以尝试下 mobx

什么是 mobx

mobx 只做一件事,解决 state 到 view 的数据更新问题

mobx 是一个库 (library),不是一个框架 (framework)。他不限制如何组织代码,在哪里保存 state 、如何处理事件,怎么发异步请求等等。我们可以回归到 Vanilla JavaScript,可以和任意类库组合使用。

核心理念

mobx 引入了几个概念,Observable state, DerivationsReactions

可以拿 Excel 表格做个比喻,Observable state 是单元格,Derivations 是计算公式,单元格的修改会触发公司的重新计算,并返回值,而最终公式的计算结果需要显示在屏幕上(比如通过图表的方式),这是 Reactions

下面通过代码理解下这些概念,以 mobx 和 react 的组合使用为例:(Open Demo on jsfiddle)

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

////////////////////
// Store

class TodoStore {
  @observable todos = [];
  @computed get completedTodosCount() {
    return this.todos.filter(todo => todo.completed === true).length;
  }
  addTodo(task) {
    this.todos.push({ task, completed: false });
  }
}

////////////////////
// Components

@observer
class TodoList extends Component {
  render() {
    const { todoStore } = this.props;
    return (
      <div>
        { todoStore.todos.map((todo, index) => <Todo todo={todo} key={index} />) }
        Progress: { todoStore.completedTodosCount }
      </div>
    );
  }
}

@observer
class Todo extends Component {
  render() {
    const { todo } = this.props;
    return (
      <li onDoubleClick={this.onRename}>
        <input
          type="checkbox"
          checked={ todo.completed }
          onChange={ this.onToggleCompleted }
        />
        { todo.task }
      </li>
    );
  }
  onToggleCompleted = () => {
    const todo = this.props.todo;
    todo.completed = !todo.completed;
  }
  onRename = () => {
    const todo = this.props.todo;
    todo.task = prompt('Task name', todo.task) || ""; 
  }
}

////////////////////
// Init

const todoStore = new TodoStore();
todoStore.addTodo('foo');
todoStore.addTodo('bar');

ReactDOM.render(
  <TodoList todoStore={todoStore} />,
  document.getElementById('mount')
);

这里通过 @observable 定义 Observable state,通过 @computed 定义 Derivations,通过 @observer 封装了 React Component 的 render 方法,这是 Reactions

为什么用 mobx

mobx 官网 罗列了不少区分与 flux 框架的优点,这里摘录一些比较打动我的。

简单

没有 connect,没有 cursor,没有 Immutable Data ... 总之感觉会少很多代码。同时概念也更少。

可以用 class 来组织和修改数据

对于组织复杂的领域模型比较适用。

可以用 JavaScript 引用来组织和修改数据

比如,可以直接

todo.completed = true;

而不需要

return todos.map((todo) => {
  if (todo.id === action.payload.id) {
    return {...todo, {completed: true}}
  else {
    return todo;
  }
});

同时也不需要引入额外的 immutable.js。

性能相比 redux 有优势

mobx 会建立虚拟推导图 (virtual derivation graph),保证最少的推导依赖。dan_abramov 亲自操刀为 todoMVC 做了极致的优化才和 mobx 打成平手。链接

不足

参考之前 redux + redux-saga 的方案,这里的一些点可能会成为你我不用他的原因。

浏览器兼容性,不支持 IE8

由于用了 reactive arrays, objects with reactive properties (getters) 这些 ES5 特性,而且这些特性不能通过 es5-shim 解决。兼容列表可参考:http://kangax.github.io/compat-table/es5/

缺少最佳实践

这部分不在 mobx 的范围之内,需要自己探索一套最佳实践。比如如何触发 action,如何组织 store,如何组织业务逻辑,如何发异步请求,如何在 React Component 之间传递数据等等。

热替换 (Hot Module Replacement)

用过 HMR,就不愿再回到手动刷页面的时代。mobx 支持 [react-transform] 的热替换方式,但是否支持 webpack 原生热替换情况下对 store 进行替换,还有待探索。

总结

mobx 简单高效,在用吐了 redux 之后,对 mobx 简直爱不释手。除了不支持 IE8 这个硬伤,其他缺点都还是可以接受的。我会在后面的小项目中应用它,并尝试探索一套最佳实践。

扩展阅读

jzlxiaohei commented 8 years ago

简单的用用,觉得真是无比清晰,基本把store的处理,简化为对model的处理。这种情况下,测试的编写也变的异常简单。

比较纠结的就是store的组织。redux就是单store,一棵state树。用mobx,很自然的有多个model,这些model要不要最后合成一个store,然后通过最上层的组件注入,还是每个组件拥有自己的domain store,app级的store再通过上层传入。

其实写redux也有类似的困惑,比如<UserList /> ,本身就与一个User数组对应,我直接扔进去,复用性是最好的,但是用redux,要通过最上层的store传下来。

现在能想到的就是,写个高阶函数,hoc(components,store)返回绑定好了的 ,这个store根据业务场景,看是否需要多个组件公用。react-router里使用这个wrapper

楼主有使用mobx写点稍大项目的经验吗,期待经验分享

daiyunchao commented 8 years ago

热更新是支持的

flyingzl commented 8 years ago

试用了一下,Vue即视感。其实既然这样,我完全可以考虑用Vue的,现在2.0性能也蛮不错的感觉

ian4hu commented 8 years ago

react想要支持IE8也需要自己完全编译react和redux相关代码,无法利用公共CDN里面的预编译的js

daiyunchao commented 7 years ago

react 要支持IE8 有一个库的

ascoders commented 7 years ago

写了一篇 Mobx 思想的实现细节,希望能对此进行补充:https://github.com/ascoders/blog/issues/16

至于 vue 既视感,要看未来浏览器对其语法支持的程度,目前 magic 逻辑太多,大型项目可能不便于维护。

dwqs commented 7 years ago

可以拿 Excel 表格做个比喻,Observable state 是单元格,Derivations 是计算公式,单元格的修改会触发公司的重新计算

这是公司 --> 公式

william-xue commented 7 years ago

可以学习一下

cllgeek commented 7 years ago

mark

jackple commented 7 years ago

请问, 在用inject的前提下, 有react-hot-loader热更新store的解决方案吗?

jackple commented 7 years ago

@daiyunchao 请问你是怎么解决store热更新的

hfuuss commented 6 years ago

期待在忙完umi的空余时间写一下 mbox最佳实践

nelhu commented 5 years ago

期待下一篇关于mobx的最佳实践

JunlinZhu-Tommy commented 4 years ago

同样期待能有一篇最佳实践。