Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MobX 入门教程 #2

Open
sorrycc opened this issue Apr 14, 2016 · 14 comments
Open

MobX 入门教程 #2

sorrycc opened this issue Apr 14, 2016 · 14 comments
Labels

Comments

@sorrycc
Copy link
Owner

sorrycc commented Apr 14, 2016

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

比如:

  • 一个 click 的事件需要经过 action, dispatch, middleware, reducer 才能走完流程
  • reducer 里不能直接修改 state,而是每次返回一个新的
  • 要区分 container 和 component
  • container 里要在 connect 里 select 数据,一不小心就选多了或选少了,出于性能考虑还要借助 reselect 这个库
  • 发异步请求需要借助 redux-thunk 或 redux-saga
  • ...

如果有,那么可以尝试下 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
Copy link

简单的用用,觉得真是无比清晰,基本把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
Copy link

热更新是支持的

@flyingzl
Copy link

flyingzl commented Nov 2, 2016

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

@ian4hu
Copy link

ian4hu commented Nov 3, 2016

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

@daiyunchao
Copy link

react 要支持IE8 有一个库的

@ascoders
Copy link

ascoders commented Mar 6, 2017

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

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

@dwqs
Copy link

dwqs commented Mar 11, 2017

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

这是公司 --> 公式

@william-xue
Copy link

可以学习一下

@cllgeek
Copy link

cllgeek commented May 17, 2017

mark

@jackple
Copy link

jackple commented Sep 27, 2017

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

@jackple
Copy link

jackple commented Sep 27, 2017

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

@hfuuss
Copy link

hfuuss commented May 29, 2018

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

@nelhu
Copy link

nelhu commented Dec 26, 2018

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

@JunlinZhu-Tommy
Copy link

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests