本来说接下来会专一学nodejs,可是最新工做又学习了一些有意思的库,於是就再写下来作个简单的入门,以前我写过一篇文章,这个也算是做為一个补充吧. javascript
此次无非就是相似笔记,把认为的一些关键点记下来,有些地方还没用到就衹是描述一下,代码有些本身写的,有些文档写的很好就搬下来,想瞭解更多可看官网
Mobx中文文档
Mobx英文文档
Github 仓库 html
PS:
2018/06/11 发现中文版有些关键地方没翻译,补充一下observable({})/observable.object(props, decorators?, options?)部分java
经过透明的响应式编程使状态管理变得简单和可扩展,背后哲学是任何源自应用状态的东西都应自动得到,(包括UI,数据序列化,服务器通信等)node
React经过提供机制把应用状态转换成可渲染组件树并对其渲染,优化UI渲染,就是经过使用虚拟DOM减小昂贵的DOM变化数量.
Mobx提供机制来存储和更新应用状态供React使用,优化应用状态和React组件同步,经过使用响应式的虚拟依赖状态图表,让其在须要的时候才更新而且保持最新.react
我的感受与Redux相比除了目的一致是管理应用状态以外.无论是写法仍是思想都大相径庭.因為才刚入门,这裡只说用法不讲区别.git
咱们先看看这段代码作了什麼,再分开详细讲解一下对应知识点github
import {observable, autorun} from 'mobx'; var todoStore = observable({ /* 一些观察的状态 */ todos: [], /* 推导值 */ get completedCount() { return this.todos.filter(todo => todo.completed).length; }, }); /* 观察状态改变的函数 */ autorun(function() { console.log( 'Completed %d of %d items', todoStore.completedCount, todoStore.todos.length ); }); /* ..以及一些改变状态的动做 */ todoStore.todos[0] = { title: 'Take a walk', completed: false, }; // -> 同步打印 'Completed 0 of 1 items' todoStore.todos[0].completed = true; // -> 同步打印 'Completed 1 of 1 items'
官方流程图:express
//标準用法 observable(value) //装饰器用法 @observable classProperty = value
Observable 值能够是JS基本数据类型、引用类型、普通对象、类实例、数组和映射. 匹配类型应用了如下转换规则,但能够经过使用调节器进行微调.编程
observable引用例如值可使用Boxed Observable observables.MobX 不会将一个有原型的对象自动转换成可观察的,由于这是它构造函数的职责.能够在constructor使用extendObservable或者类型定义使用decorate替代.json
values: 能够是对象、 数组或者字符串键的 ES6 map;
options:
const map = observable.map(new Map());
如下是MobX 提供方法:
这是递归的,因此数组中的全部(将来的)值都会是可观察的.
options:
const ary = observable.array([1, 2, 4]);
注意:
observable.array 会建立一我的造数组(类数组对象)来代替真正的数组. 支持全部的原生方法,包括从索引的分配到包含数组长度.
如下是MobX 提供方法:
一个普通的 JavaScript 对象 (指不是使用构造函数建立出来的对象,而是以 Object 做为其原型,或者根本没有原型)传递给 observable 方法,对象的全部属性都将被拷贝至一个克隆对象并将克隆对象转变成可观察的.
这是递归应用的,因此若是对象的某个值是一个对象或数组,那么该值也将经过 observable 传递.
options:
const obj = observable.object({ key: "value"});
注意:
JavaScript 中的全部原始类型值都是不可变的,所以它们都是不可观察的,box建立一个基于 ref 装饰器的箱子.这意味着箱子里的任何(未来)值都不会自动地转换成 observable .
options:
1) name: 调试名称,用于 spy 或者 MobX 开发者工具;
const box = observable.box('box'); box.observe(function(change) { console.log(change.oldValue, '->', change.newValue); });
如下是box提供方法:
定义 observable 属性的行为,默认为对任意键值对使用 observable.deep,对 getters 使用 computed .
class Person { name = 'John'; } // 使用 decorate 时,全部字段都应该指定 (毕竟,类里的非 observable 字段可能会更多) decorate(Person, { name: observable, });
任何源自状态而且不会再有任何进一步的相互做用的东西就是衍生,衍生以多种形式存在:
MobX 区分两种类型的衍生:
计算值(computed values)是能够根据现有的状态或其它计算值衍生出的值.若是你想响应式的产生一个能够被其它 observer 使用的值,请使用 @computed.计算值在大多数状况下能够被 MobX 优化的,例如:
注意:
计算值的 setter:
class Test { @observable num = 0; @computed get total() { return this.num * 10; } set total(value) { this.num = value / 10; } } //OR const Test = observable.object({ num: 0; get total() { return this.num * 10; } set total(value) { this.num = value / 10; } })
某些状况下,你须要传递一个“在box中”的计算值时,它多是有用的.
options:
MobX 提供了三个内置 comparer (比较器) :
const box = observable('box'), upperCaseName = computed(() => name.get().toUpperCase()), disposer = upperCaseName.observe(change => console.log(change.newValue)); box.set('Dave');
建立一个响应式函数,而该函数自己永远不会有观察者,调用后将接收一个参数,即当前 reaction(autorun),可用于在执行期间清理 autorun. 当使用 autorun 时,所提供的函数老是当即被触发一次,而后每次它的依赖关系改变时会再次被触发,相比之下computed(function)建立的函数只有当它有本身的观察者时才会从新计算,不然它的值会被认为是不相关的;
options:
var numbers = observable([1, 2, 3]); var sum = computed(() => numbers.reduce((a, b) => a + b, 0)); var disposer = autorun(() => console.log(sum.get())); // 输出 '6' numbers.push(4); // 输出 '10' disposer(); //清理 autorun numbers.push(5); // 不会再输出任何值.`sum` 不会再从新计算.
when 观察并运行给定的 predicate,直到返回true. 一旦返回 true,给定的 effect 就会被执行,而后 autorunner(自动运行程序) 会被清理. 该函数返回一个清理器以提早取消自动运行程序.
class MyResource { constructor() { when( // 一旦... () => !this.isVisible, // ... 而后 () => this.dispose() ); } @computed get isVisible() { // 标识此项是否可见 } dispose() { // 清理 } }
若是没提供 effect 函数,when 会返回一个 Promise .它与 async / await 能够完美结合.
async function() { await when(() => that.isVisible) // 等等.. }
第一个数据函数是用来追踪并返回数据做为第二个做用函数的入参. 不一样于 autorun 的是当建立时函数不会直接运行,只有在数据表达式首次返回一个新值后才会运行. 在执行做用函数时访问的任何 observable 都不会被追踪.
reaction 返回一个清理函数,接收两个参数,即当前的 reaction,能够用来在执行期间清理 reaction .
options:
const todos = observable([ { title: "test1" }, { title: "test2" } ]); //会对长度变化做出反应 reaction(() => todos.length, length => console.log("reaction 1:", todos.map(todo => todo.title).join(", "))); //会对某个 todo 的 title 变化做出反应 reaction(() => todos.map(todo => todo.title), titles => console.log("reaction 2:", titles.join(", "))); // autorun 对它函数中使用的任何东西做出反应 autorun(() => console.log("autorun:", todos.map(todo => todo.title).join(", "))); // 输出: // autorun: test1 action(() => { todos.push({title: "test3"}); })() // 输出: // autorun: test1, test2, test3 // reaction 2: test1, test2, test3 // reaction 1: test1, test2, test3 action(() => { todos[1].title = 'test4'; })() // 输出: // autorun: test1, test4, test3 // reaction 2: test1, test4, test3
observer 函数/装饰器能够用来将 React 组件/无状态函数组件转变成响应式组件. 它用 mobx.autorun 包装了组件的 render 函数以确保任何组件渲染中使用的数据变化时均可以强制刷新组件.
import {observer} from "mobx-react"; var timerData = observable({ secondsPassed: 0 }); setInterval(() => { timerData.secondsPassed++; }, 1000); @observer class Timer extends React.Component { render() { return (<span>Seconds passed: { this.props.timerData.secondsPassed } </span> ) } }; //OR const Timer = observer(({ timerData }) => <span>Seconds passed: { timerData.secondsPassed } </span> );
@observer class Timer extends React.Component { @observable secondsPassed = 0 componentWillMount() { setInterval(() => { this.secondsPassed++ }, 1000) } render() { return (<span>Seconds passed: { this.secondsPassed } </span> ) } }
在React组件上引入可观察属性. 这意味着你能够在组件中拥有功能一样强大的本地状态(local state),而不须要经过 React 的冗长和强制性的 setState 机制来管理. 响应式状态会被 render 提取调用,但不会调用其它 React 的生命周期方法,除了 componentWillUpdate 和 componentDidUpdate . 若是你须要用到其余 React 生命周期方法 ,只需使用基于 state 的常规 React API 便可.
const App = () => <Provider colors={colors}> <app stuff... /> </Provider>; const Button = inject("colors")(observer(({ colors, label }) => <button style={{ color: colors.foreground }} >{label}<button> ));
当组件从新渲染时被触发,这使得它很容易追溯渲染并找到致使渲染的操做(action).
action(fn)
action(name, fn)
@action classMethod() {}
@action(name) classMethod () {}
@action boundClassMethod = (args) => { body }
@action(name) boundClassMethod = (args) => { body }
@action.bound classMethod() {}
action能够是任何用来修改状态的东西,只执行查找,过滤器等函数不该该被标记为action,以容许 MobX 跟踪它们的调用.能够有助于更好的组织代码.
自动地将动做绑定到目标对象.与 action 不一样的是不须要一个name参数,名称将始终基于动做绑定的属性.
因為箭头函数已是绑定过的而且不能从新绑定,因此不能一块儿使用
class Ticker { @observable tick = 0 @action.bound increment() { this.tick++ // 'this' 永远都是正确的 } }
action 包装/装饰器只会对当前运行的函数做出反应,而不会对当前运行函数所调用的函数(不包含在当前函数以内)做出反应! 这意味着若是 action 中存在 setTimeout、promise 的 then 或 async 语句,而且在回调函数中某些状态改变了,那么这些回调函数也应该包装在 action 中.
错误写法,抛出异常
class TrafficLight { @observable status = "yellow" // "red" / "green" / "yellow" @action fetchProjects() { this.status = "yellow" toggleLight().then( res => { this.status = "green" }, err => { this.status = "red" } ) } }
//包装修復在action
class TrafficLight { @observable status = "yellow" // "red" / "green" / "yellow" @action handleAjax() { this.status = "yellow" toggleLight().then( this.handleSuc, this.handleErr ) } @action.bound handleSuc(res){ this.status = "green" } @action.bound handleErr(err){ this.status = "red" } }
//另外一种内嵌写法
class TrafficLight { @observable status = "yellow" // "red" / "green" / "yellow" @action handleAjax() { this.status = "yellow" toggleLight().then( action('handleSuc',res => { this.status = "green" }), action('handleErr',res => { this.status = "red" }) ) } }
runInAction(name?, thunk)
runInAction 是个简单的工具函数,它接收代码块并在(异步的)动做中执行.这对于即时建立和执行动做很是有用.
class TrafficLight { @observable status = "yellow" // "red" / "green" / "yellow" @action handleAjax() { this.status = "yellow" toggleLight().then( runInAction(res => { this.status = "green" }), runInAction(res => { this.status = "red" }) ) } }
async / await
async / await 只是围绕基于 promise 过程的语法糖. 结果是 @action 仅应用于代码块,直到第一个 await . 在每一个 await 以后,一个新的异步函数将启动,因此在每一个 await 以后,状态修改代码应该被包装成动做.
class TrafficLight { @observable status = "yellow" // "red" / "green" / "yellow" @action async handleAjax() { this.status = "yellow" toggleLight().then( try{ const result = await dosometings(); runInAction(res => { this.status = result; }), }catch(err){ runInAction(res => { this.status = "red"; }) } ) } }
flow内置概念
优势是它在语法上基本与 async / await 是相同的 (只是关键字不一样),而且不须要手动用 @action 来包装异步代码,这样代码更简洁.
class TrafficLight { @observable status = "yellow" // "red" / "green" / "yellow" @action handleAjax = flow(function* () { this.status = "yellow" toggleLight().then( try{ const result = yield dosometings(); this.status = result; }catch(err){ this.status = "red"; } ) }) }
这些 API 都是响应式的,这意味着若是使用 set 进行添加,使用 values 或 keys 进行迭代,即使是新属性的声明均可以被 MobX 检测到.
import { get, set, observable, values } from "mobx" const twitterUrls = observable.object({ "John": "twitter.com/johnny" }) autorun(() => { console.log(get(twitterUrls, "Sara")) // get 能够追踪还没有存在的属性 }) autorun(() => { console.log("All urls: " + values(twitterUrls).join(", ")) }) set(twitterUrls, { "Sara" : "twitter.com/horsejs"})
不想摘抄了,看文档吧...
Mobx工具函数
不想摘抄了,看文档吧...
Mobx贴士与技巧
(更多内容请自行查阅,本节到此为止了.)