Open purplebamboo opened 7 years ago
mobx是简单、可扩展的状态管理库。 mobx的核心是TFRP,透明的函数响应式编程(transparently applying functional reactive programming) mobx认为任何源自应用状态的东西都应该自动地获得。
我们通过一个简单的计数器看mobx的实例。这边的例子不包含与react的结合。
import { observable, autorun } from 'mobx'; // let { observable, autorun } = mobx; // 注入观察钩子 let counter = observable({number:0}); // 运行一次,建立依赖 autorun(() => { console.log('number:' + counter.number) }); setTimeout(function(){ counter.number ++ },100) // 结果为: // number:0 // number:1
在线编辑:https://jsfiddle.net/mweststrate/wv3yopo0/
两个概念:
其实粗略的可以想到原理:
mobx一个很大的特色是可以使用es7的注解增强可读性。我们先回顾下javascript的注解使用方式。虽然还没有完全定稿,不过可以使用babel转义使用。
参考: https://aotu.io/notes/2016/10/24/decorator/index.html
class Cat { say() { console.log("meow ~"); } }
等价于:
function Cat() {} Object.defineProperty(Cat.prototype, "say", { value: function() { console.log("meow ~"); }, enumerable: false, configurable: true, writable: true });
function isAnimal(target) { target.isAnimal = true; return target; } @isAnimal class Cat { ... } console.log(Cat.isAnimal); // true
Cat = isAnimal(function Cat() { ... });
function readonly(target, name, descriptor) { discriptor.writable = false; return discriptor; } class Cat { @readonly say() { console.log("meow ~"); } } var kitty = new Cat(); kitty.say = function() { console.log("woof !"); } kitty.say() // meow ~
function Cat() { ... } let descriptor = { value: function() { console.log("meow ~"); }, enumerable: false, configurable: true, writable: true }; descriptor = readonly(Cat.prototype, "say", descriptor) || descriptor; Object.defineProperty(Cat.prototype, "say", descriptor);
所以属性注解拿到的是 prototype,name,descriptor
prototype
name
descriptor
于是我们看下,注解写法的计数器例子
import { observable, autorun,computed } from 'mobx'; class Counter { @observable number = 0; @computed get msg() { return 'number:' + this.number } } var store = new Counter() // 运行一次,建立依赖 autorun(() => { console.log(store.msg) }); // 做出改动 setTimeout(function(){ store.number ++ },100)
多了几个概念:
@computed
import { observer } from 'react-mobx'; import React, { Component } from 'react'; import { observable, autorun,computed } from 'mobx'; class Counter { @observable number = 0; @computed get msg() { return 'number:' + this.number } } var store = new Counter() @observer class App extends Component { render() { return (<div> { store.msg } <br /> <button onClick={this.handleInc}> + </button> <button onClick={this.handleDec}> - </button> </div>); } handleInc() { store.number ++ ; } handleDec() { store.number -- ; } } ReactDOM.render(<App />, document.getElementById('root'));
@observer是一个注解,本质上是用 mobx.autorun 包装了组件的 render 函数以确保任何组件渲染中使用的数据变化时都可以强制刷新组件
mobx之前一个比较大的问题是可以随意修改store,后来引入了 action解决这个问题。
action做了这几个事情:
例子改写:
import { observer } from 'react-mobx'; import React, { Component } from 'react'; import { observable, autorun,computed } from 'mobx'; class Counter { @observable number = 0; @computed get msg() { return 'number:' + this.number } @action increment: () => { this.number ++ } @action decrement: () => { this.number -- } } var store = new Counter() @observer class App extends Component { render() { return (<div> { store.msg } <br /> <button onClick={this.handleInc}> + </button> <button onClick={this.handleDec}> - </button> </div>); } handleInc() { store.increment(); } handleDec() { store.decrement(); } } ReactDOM.render(<App />, document.getElementById('root'));
这样就比较安全了。
至此,mobx的整个流程就出来了:
mobx的哲学:
类似redux这样的粗粒度的订阅很容易出现超额订阅的问题:
view() { if (count === 0) { return a; } else { return b; } }
基于 redux 的方案,我们必须同时监听 count, a 和 b 。在 counte === 0 的时候,b 如果修改了,也会触发 view 。而这个时候的 b 其实是无意义的。
view() { todos[0].title }
基于 redux,我们通常会订阅 todos,这样 todos 的新增、删除都会触发 view 。其实这里真正需要监听的是 todos 第一个元素的 title 属性是否有修改。
与之对应的mobx的运行时依赖,可以做到最小力度。
import { observable, autorun } from 'mobx'; const counter = observable(0); const foo = observable(0); const bar = observable(0); autorun(() => { if (counter.get() === 0) { console.log('foo', foo.get()); } else { console.log('bar', bar.get()); } }); bar.set(10); // 不触发 autorun counter.set(1); // 触发 autorun foo.set(100); // 不触发 autorun bar.set(100); // 触发 autorun
执行结果:
foo 0 bar 10 bar 100
vue其实跟mobx做的事情类似。
Redux 与 MobX 的不同主要集中于以下几点:
mobx的工程例子: https://github.com/gothinkster/realworld
mobx 不需要自己管理订阅,可以像vue那样直接帮你解析出依赖,数据流修改起来很自然。而redux的数据流更清晰,一个完整的数据流闭环规范,小项目使用mobx感觉会像vue那样很简单快速,但是大项目还是像redux那样更清晰。目前mobx的社区也没有redux活跃,缺少一些最佳实践。目前来看redux还是react下最合适的选择。
相关引用:
可以考虑在readme里搞个目录
mark
大神是阿里的吗
mobx初探
mobx是简单、可扩展的状态管理库。 mobx的核心是TFRP,透明的函数响应式编程(transparently applying functional reactive programming) mobx认为任何源自应用状态的东西都应该自动地获得。
基本实例
我们通过一个简单的计数器看mobx的实例。这边的例子不包含与react的结合。
在线编辑:https://jsfiddle.net/mweststrate/wv3yopo0/
两个概念:
其实粗略的可以想到原理:
注解的概念与用法
mobx一个很大的特色是可以使用es7的注解增强可读性。我们先回顾下javascript的注解使用方式。虽然还没有完全定稿,不过可以使用babel转义使用。
参考: https://aotu.io/notes/2016/10/24/decorator/index.html
类是个语法糖
等价于:
类注解
等价于:
属性注解
等价于:
所以属性注解拿到的是
prototype
,name
,descriptor
例子的注解写法
于是我们看下,注解写法的计数器例子
多了几个概念:
@computed
的执行,也会进行依赖收集。与react结合使用
@observer是一个注解,本质上是用 mobx.autorun 包装了组件的 render 函数以确保任何组件渲染中使用的数据变化时都可以强制刷新组件
mobx的action
mobx之前一个比较大的问题是可以随意修改store,后来引入了 action解决这个问题。
action做了这几个事情:
例子改写:
这样就比较安全了。
至此,mobx的整个流程就出来了:
mobx的特点
mobx的哲学:
超额订阅
类似redux这样的粗粒度的订阅很容易出现超额订阅的问题:
基于 redux 的方案,我们必须同时监听 count, a 和 b 。在 counte === 0 的时候,b 如果修改了,也会触发 view 。而这个时候的 b 其实是无意义的。
基于 redux,我们通常会订阅 todos,这样 todos 的新增、删除都会触发 view 。其实这里真正需要监听的是 todos 第一个元素的 title 属性是否有修改。
运行时依赖
与之对应的mobx的运行时依赖,可以做到最小力度。
执行结果:
与vue类比
vue其实跟mobx做的事情类似。
与redux的对比
Redux 与 MobX 的不同主要集中于以下几点:
详细的例子
mobx的工程例子: https://github.com/gothinkster/realworld
结论
mobx 不需要自己管理订阅,可以像vue那样直接帮你解析出依赖,数据流修改起来很自然。而redux的数据流更清晰,一个完整的数据流闭环规范,小项目使用mobx感觉会像vue那样很简单快速,但是大项目还是像redux那样更清晰。目前mobx的社区也没有redux活跃,缺少一些最佳实践。目前来看redux还是react下最合适的选择。
相关引用: