之前几篇大概介绍了mobx最常用的几个方法,这次准备把剩余的公共方法都介绍了。
autorunAsync(debugName?: string, action: () => void, minimumDelay?: number, scope?): disposer
autorunAsync
的功能与 autorun
相似,功能都是在观测对象发生变化时自动运行回调函数 action
。不同点在于 autorun
是在观测对象发生变化时立即执行的,而 autorunAsync
是异步的,可以通过 minimumDelay
参数来指定延迟的时间。
如果被观测对象的在延迟过程中发生多次变化, action
也只会在延迟结束时触发一次,所以它和后面要介绍到的 transaction
方法效果类似。在某些场景下这个方法很有用,比如他可以被用来防止频繁向服务端发起请求。
如果传了 scope
参数,那么 scope
将作为 action
运行时的this。
如果传了第一个参数 debugName
,那么在调试工具中将使用 debugName
作为调试信息。
和 autorun
一样, autorunAsync
也会返回一个销毁函数。
autorunAsync(()=> { // 我们假设 searchBar.keyword 已经被观测, 是搜索输入框的值。当它发生变化时我们要把它发送到服务端请求搜索结果。 // 如果这里使用autorun,那么每次变化都会向调用sendKeywordToServer。 // 使用autorunAsync延迟300ms发送,当发送时,searchBar.keyword会是这300ms内变化的最终值。 // 这样就可以有效的防止频繁请求造成服务抖动。 sendKeywordToServer(searchBar.keyword); }, 300);
有些时候,你可能想要有更多的数据结构或其他的东西(比如streams),也可以用于响应计算。可以使用 Atom
类简单快速的实现这一功能。 Atom
实例可以通知mobx观测对象发生了变化,而mobx会在启用和停用观测对象的时候通知 Atom
实例。
下面的例子展示了 Atom
的全部功能,这个例子展示了如何创建一个时钟,这个时钟只有在被观测时才会运行。
import {Atom, autorun} from "mobx"; classClock{ atom; intervalHandler = null; currentDateTime; constructor() { // 创建一个Atom实例 this.atom = new Atom( // 第一个参数: Atom实例的名字, 调试用的 "Clock", // 第二个参数(可选): 从不被监听到被监听时的回调函数. () => this.startTicking(), // 第三个参数(可选): 从被监听到不被监听时的回调函数 // 注意,atom实例会多次在这两种状态见转换 () => this.stopTicking() ); } getTime() { // 如果Atom实例被响应函数调用,则reportObserved返回true。 // 同时,reportObserved会通知mobx这个实例在响应回调中被使用了,它还会触发实例的第二个参数(startTicking) if (this.atom.reportObserved()) { return this.currentDateTime; } else { // 当没有响应函数调用Atom实例的时候,就不会触发startTicking。 // 根据不同的情况,这里也可以做不同的处理,比如抛出一个错误,返回一个默认值等等。 return new Date(); } } tick() { this.currentDateTime = new Date(); // 通知mobx当前值发生了变化 this.atom.reportChanged(); } startTicking() { this.tick(); // 初始化时钟 this.intervalHandler = setInterval( ()=> this.tick(), 1000 ); } stopTicking() { clearInterval(this.intervalHandler); this.intervalHandler = null; } } const clock = new Clock(); const disposer = autorun(()=> console.log(clock.getTime())); // ... 每秒打印时间 disposer(); // 停止打印。如果没有响应函数调用当前clock实例,那么时钟将停止。会触发stopTicking函数。
使用 Reaction
可以创建一个自定义的监听器。 Reaction
接受一个函数作为参数,他会分析这个函数所依赖的被观测对象,然后追踪他们,当他们发生变化时发出事件。
下面的例子展示了 autorun
是如何用 Reaction
实现的,其实这个例子我没看太懂,貌似必须调用 Reaction
的track方法才能追踪并发出信号,但是例子中是在 Reaction
接收的函数中调用,然后runReaction的时候开始,具体的得等我翻了源码之后才能知道了……
export functionautorun(view: Lambda, scope?: any){ if (scope) view = view.bind(scope); const reaction = new Reaction(view.name || "Autorun", function(){ this.track(view); }); // 开始或者排入队列 if (isComputingDerivation() || globalState.inTransaction > 0) globalState.pendingReactions.push(reaction); else reaction.runReaction(); return reaction.getDisposer(); }
expr(worker: () => void)
expr
可以在计算属性的函数中创建一个临时的计算属性,其实就是 computed(func).get()
。作者在文档中说设计这个api的意图是为了提升计算属性的性能,比如下面的例子,如果使用 expr
替代直接用比较运算,可以利用计算属性的缓存,减少运算次数。
const TodoView = observer(({todo, editorState}) => { const isSelected = mobx.expr(()=> editorState.selection === todo); return <div className={isSelected ? "todo todo-selected" : "todo"}>{todo.title}</div>; });
extendObservable(target: object, ...properties: object)
在之前的几篇文章中,我们已经大概见过 extendObservable
应用的实例了。 extendObservable
和 Object.assign
类似,接受多个参数,将 properties
上所有的键值对,都合并到 target
上,同时把它们都转换成可观测的属性。
如果属性值是一个没有参数的函数,那 extendObservable
将用 computed
把它转化为一个计算属性。
所以, observable(object)
其实是 extendObservable(object, object)
的别名。
var Person = function(firstName, lastName){ // 在当前实例为观测对象 extendObservable(this, { firstName: firstName, lastName: lastName }); } var matthew = new Person("Zheng", "Xingcheng"); // 向观测对象上添加属性 extendObservable(matthew, { age: 30 });
isObservable(testValue:object, propertyName?: string)
isObservable
是用来判断一个变量是不是用observable观测对象的,如果是就会返回true,如果想看变量的某个属性是否可观测,直接传入属性的引用是不行的,需要传第二个参数 propertyName
指定要判断哪个属性,如果属性可观测,就返回true
var person = observable({ firstName: "Zheng", lastName: "Xingcheng" }); person.age = 30; console.log(isObservable(person)); // true console.log(isObservable(person, "firstName")); // true console.log(isObservable(person.firstName)); // false (just a string) console.log(isObservable(person, "age")); // false
为了细化各种类型的判断,mobx还提供了map,array,object三种类型的判断,比起 isObservable
,他们的判断标准更严格,如果类型不符合就会返回false。
isObservableMap(testValue:object)
如果 testValue
是用 mobx.map
创建的对象,则返回true。
isObservableArray(testValue:object)
如果 testValue
是用 mobx.observable(array)
创建的对象,则返回true。
isObservableObject(testValue:object)
如果 testValue
是用 mobx.observable(object)
创建的对象,则返回true。
var testValue = observable({ arr: [1, 2, 3], obj: { x: 1 }, map: map([['y',2]]) }); console.log(isObservableMap(testValue.map)); // true console.log(isObservableArray(testValue.arr)); // true console.log(isObservableObject(testValue.obj)); // true
spy(listener)
spy
可以注册一个全局的监听函数,监听所有的mobx发出的事件,通常是用来做log或者做调试的。
比如以下例子,会打印所有的action:
spy((event) => { if (event.type === 'action') { console.log(`${event.name}with args:${event.arguments}`) } })
不同的操作,event也会不一样,下面的表格是每种事件对应的参数:
event | event带的属性 | 是否可以嵌套发生 |
---|---|---|
action | name, target (scope), arguments, fn (source function of the action) | yes |
transaction | name, target (scope) | yes |
scheduled-reaction | object (Reaction instance) | no |
reaction | object (Reaction instance), fn (source of the reaction) | yes |
compute | object (ComputedValue instance), target (scope), fn (source) | no |
error | message | no |
update (array) | object (the array), index, newValue, oldValue | yes |
update (map) | object (observable map instance), name, newValue, oldValue | yes |
update (object) | object (instance), name, newValue, oldValue | yes |
splice (array) | object (the array), index, added, removed, addedCount, removedCount | yes |
add (map) | object, name, newValue | yes |
add (object) | object, name, newValue | yes |
delete (map) | object, name, oldValue | yes |
create (boxed observable) | object (ObservableValue instance), newValue | yes |
toJS(value: any, supportCycles?=true: boolean)
toJS可以将一个observableObject下的转化为javascript原生的对象。他会递归转换array,object,map和基础类型的值,但是不会转换计算属性和其他不可枚举的值。默认情况下,toJS会缓存下每次运行的值,貌似作者设计这个api就是为了输出log用的,可以设置 supportCycles
参数为false来提高toJS的性能。
对于更复杂的序列化反序列化场景,mobx的作者推荐使用他开发的 serializr 库。
transaction(worker: () => void)
在之前的 autorunAsync
有提到过,除了 autorunAsync
,还可以使用 transaction
来做批量处理。
transaction
用来批处理一系列的更新,而不会通知观测对象,当所有更新结束,才会发出通知。 transaction
接收一个没有参数的worker函数作为参数,在这个函数执行完成之前,不会通知观察者。 transaction
的返回值就是worker函数的返回值。另外 transaction
是同步的,可以被嵌套,只有最外层的 transaction
执行完,才会触发响应。
import {observable, transaction, autorun} from "mobx"; const numbers = observable([]); autorun(()=> console.log(numbers.length, "numbers!")); // Prints: '0 numbers!' transaction(()=> { transaction(()=> { numbers.push(1); numbers.push(2); }); numbers.push(3); }); // Prints: '3 numbers!'
untracked(fn: () => void)
使用 untracked
可以创建一个不被观测的代码块,通常 untracked
需要放在 (@)action
里面才有意义,比如以下的例子:
const person = observable({ firstName: "Michel", lastName: "Weststrate" }); autorun(()=> { console.log( person.lastName, ",", // person.firstName放在了untracked的回调里面,所以不会跟这个autorun的监听函数绑定到一起 // 在修改person.firstName时就不会触发这个监听函数 untracked(()=> person.firstName) ); }); // prints: Weststrate, Michel person.firstName = "G.K."; // doesn't print! person.lastName = "Chesterton"; // prints: Chesterton, G.K.
when(debugName?, predicate: () => boolean, effect: () => void, scope?)
when
会感测并运行参数predicate,predicate有点类似一个计算属性,当predicate为true的时候,则自动运行effect,然后销毁自己。所以 when
是一个只运行一次的 autorun
。
下面这个例子展示了用 when
来实现自动销毁组件的功能:
classMyResource{ constructor() { when( // 当断言为真... () => !this.isVisible, // ... 则运行一次然后销毁 () => this.dispose() ); } @computed get isVisible() { // 返回组件是否可见 } dispose() { // 销毁组件 } }
本篇整理了下mobx的公共方法和作用。就此,基础api算是都介绍完了。之后会再着重写些使用方法和介绍mobx原理的内容。
话说最近懒癌又开始发作了……_(:3 」∠)_……看着朋友们跟打了鸡血一样写那么多blog好着急的说……希望以后能迎头赶上吧……