Proxy 支持
默认情况下,MobX 使用 Proxy 代理方式来让数组以及纯对象可观察。Proxy 方式能够提供最佳的性能表现以及在不同环境下大多数行为的一致性。 但是如过你的目标环境不支持 Proxy,你也可以通过配置将 Proxy 支持关闭。 这种情况大部分是由于你需要支持IE或 没有使用Hermes引擎的 React Native环境。
使用configure
方法来关闭Proxy支持
import { configure } from "mobx"
configure({
useProxies: "never"
})
useProxies
属性可被设置的值如下:
"always"
(默认值): MobX 只能运行在支持Proxy
的环境中,如果环境不支持 Proxy 将报错。"never"
: Proxy 模式将不会被使用,MobX降级到non-proxy
替代方案。 兼容 ES5 环境, 但是会带来一些限制 limitations."ifavailable"
(实验阶段): 如果环境支持则启用 Proxy,否则 降级到non-proxy
替代方案。这个模式的优势是:MobX将对不能在ES5环境中使用的API以及语言特性发出警告,触发ES5标准限制时抛出错误。
注意: 在MobX 6 之前,你需要面临 MobX 4(兼容历史老旧引擎) 还是 MobX 5(支持现代引擎)的选择,然而现在MobX 6 将根据你的引擎环境引入特定API的polyfill
,(比如在只支持ES5标准的环境中使用map方法)。Proxy 不能被 polyfilled,虽然目前确实已经有 这样的 polyfill垫片了,但是他们并不能支持所有的场景,因此也不适用于 MobX,不要使用他们。
关闭 Proxy 支持情况下的使用限制
1.可观察的数组不再是真正的数组,因此使用 Array.isArray
方法时将会返回 false
。实际场景中,你在传递数组给其他模块时需要先使用.slice()
操作来为原始数组创建一份浅拷贝,举个例子,concat
操作在可观察数组上时将不会生效,因此你需要先使用.slice()
。
3.在创建可观察的纯对象之后,对其进行添加/删除的属性操作自动观察将不会生效。如果你想将通过index类数组下标的方式访问对象或者其他动态集合请使用可观察的Maps来替代。
在不支持Proxy情况下,也是有方法使这些(add/delete)动态操作观察生效的。那就是通过Collection utilities {🚀}工具集。你需要确保1.新属性的添加是通过工具集的set
方法 2.使用工具集的 values
/keys
/entries
来迭代对象 而不是 JavaScript内置方法。
但是由于这经常会被忘记,所以我们还是推荐尽量使用 可观察的Maps来替代。
装饰器
开启装饰器支持(实验性功能)请查看 Enabling decorators {🚀} 章节。
代码风格选项
为了帮助你更好地接受MobX所倡导的代码规范:严格地区分 actions
,state
以及derivations
,当你的代码触发某些规则时,MobX可以在运行时对其进行"格式化"。
请确保你的MobX代码规范尽可能严格,采取下面的设置并仔细阅读对应说明。
import { configure } from "mobx"
configure({
enforceActions: "always",
computedRequiresReaction: true,
reactionRequiresObservable: true,
observableRequiresReaction: true,
disableErrorBoundaries: true
})
有时你会发现这个级别的严格程度会带来一些困扰,出于提高生产力的目的,在确保你(或者你的团队,公司)足够了解MobX的心智模型之后,适时地禁用某些规则也是可以的。
也有一些情况之下,你想要去除这些规则带来的警告,(比如当你的代码包含于runInAction
时),对于推荐规范来说这也是没问题的。
不用过于死板。
enforceActions
enforceActions 配置的目的是让你不会忘记在 action
包裹事件处理函数。
可供选择的配置:
"observed"
(默认值): 可观察状态必须通过actions
来修改。 这是默认选项,对于有一定复杂度的应用来说这是推荐的严格模式。"never"
: 状态可以在任何地方被修改。"always"
: 任何状态都能只能通过actions
来修改,在实际开发中也包括新建状态.
"observed"
带来的好处是它允许你可以在actions
外部创建可观察的对象然后自由地修改,即使这些对象还没有被使用。
由于状态在原则上总是被一些事件处理函数所创建,并且事件处理函数总是要被另外一个函数再包一层,因此设置本字段为always
是理论上最优的。但是,你大概率不会想在单元测试中使用它。
在极少数情况下,你可能惰性创造了某些可被观察的对象,比如说在一个计算属性中,你可以使用runInAction
将创建可观察对象的高阶ad-hoc
函数包起来。
computedRequiresReaction: boolean
禁止从action
或者reaction
之外,直接获取未被观察的计算属性的值。
这保证你将不会使用某个MobX未缓存的响应计算的值。默认值: false
。
在下面这个例子中,MobX在第一个代码块中将不会缓存响应计算的值,但是会在第二以及第三个代码块中缓存结果:
class Clock {
seconds = 0
get milliseconds() {
console.log("computing")
return this.seconds * 1000
}
constructor() {
makeAutoObservable(this)
}
}
const clock = new Clock()
{
// This would compute twice, but is warned against by this flag.
console.log(clock.milliseconds)
console.log(clock.milliseconds)
}
{
runInAction(() => {
// Will compute only once.
console.log(clock.milliseconds)
console.log(clock.milliseconds)
})
}
{
autorun(() => {
// Will compute only once.
console.log(clock.milliseconds)
console.log(clock.milliseconds)
})
}
observableRequiresReaction: boolean
当未被观察对象以可观察方式访问时发出警告。
这可以帮助你检测是否在“MobX 上下文”中使用可观察对象。
这是一个非常好的方式去发现被遗忘的observer
包裹函数 ,比如在一个React组件中。此外它也会发现那些丢失的actions
。默认值: false
configure({ observableRequiresReaction: true })
注意: 当使用observer
处理组件propTypes
时,这条规则可能会带来一些副作用。
reactionRequiresObservable: boolean
当一个reaction
(比如:autorun
)被创建时不包含任何observable
对象发出警告。
使用和这个设置可以帮助你检查是否有必要用observer
包裹React组件,用action
包裹函数,以及发现那些单纯忘记将某些数据结构或者对象属性observer
化的场景。 默认着: false
configure({ reactionRequiresObservable: true })
disableErrorBoundaries: boolean
默认情况下,MobX会捕获并重新抛出那些发生在你代码中的异常来确保一个异常中的reaction
不会阻止其他调度的执行,大多数情况下是不相关的其他的reaction
。这意味着这些异常无法追溯到原始的代码段落,因此你无法通过try/catch的方式来捕获它们。
通过禁用错误边界处理,异常能够逃逸捕获,这也许有利于debugg,但是可能会使你MobX,进一步扩展到你的应用到一个无法复原的错误状态。默认值: false
。
这个选项对于单元测试来说是非常好的,但是在每一个测试case之后记得调用_resetGlobalState
,比如说在jest中使用afterEach
:
import { _resetGlobalState, observable, autorun, configure } from "mobx"
configure({ disableErrorBoundaries: true })
test("Throw if age is negative", () => {
expect(() => {
const age = observable.box(10)
autorun(() => {
if (age.get() < 0) throw new Error("Age should not be negative")
})
age.set(-1)
}).toThrow("Age should not be negative")
})
afterEach(() => {
_resetGlobalState()
})
safeDescriptors: boolean
MobX让某些字段不可枚举或不可写来阻止你做一些不支持的可能会导致代码问题的行为。
MobX makes some fields non-configurable or non-writable to prevent you from doing things that are not supported or would most likely break your code. However this can also prevent spying/mocking/stubbing in your tests.
configure({ safeDescriptors: false })
disables this safety measure, making everything configurable and writable.
Note it doesn't affect existing observables, only the ones created after it's been configured.
Use with caution and only when needed - do not turn this off globally for all tests, otherwise you risk false positives (passing tests with broken code). Default: true
configure({ safeDescriptors: false })
Further configuration options
isolateGlobalState: boolean
Isolates the global state of MobX when there are multiple instances of MobX active in the same environment. This is useful when you have an encapsulated library that is using MobX, living in the same page as the app that is using MobX. The reactivity inside the library will remain self-contained when you call configure({ isolateGlobalState: true })
from it.
Without this option, if multiple MobX instances are active, their internal state will be shared. The benefit is that observables from both instances work together, the downside is that the MobX versions have to match. Default: false
configure({ isolateGlobalState: true })
reactionScheduler: (f: () => void) => void
设置一个新的函数来执行所有MobX中的reactions
。
在默认情况下reactionScheduler
只会f
函数而不发生其他行为。
这对于基础的debug需求或者减慢reactions
来优先建立应用视图更新来说非常有用。默认值=> f()`
configure({
reactionScheduler: (f): void => {
console.log("Running an event after a delay:", f)
setTimeout(f, 100)
}
})