本文由Michel Weststrate和Aaron Boyer同行评审。感谢所有SitePoint的同行评审员,使SitePoint的内容尽善尽美!
如果您曾经使用jQuery编写过比非常简单的应用程序更复杂的应用程序,您可能遇到过保持UI不同部分同步的问题。通常,数据的更改需要反映在多个位置,并且随着应用程序的增长,您可能会发现自己陷入困境。为了控制这种混乱,通常使用事件来让应用程序的不同部分知道何时发生了更改。
那么,您今天是如何管理应用程序状态的呢?我要冒昧地说,您过度订阅了更改。没错。我什至不认识你,但我就要指出来。如果您没有过度订阅,那么我敢肯定您工作太努力了。
当然,除非您使用MobX……
关键要点
autorun
功能,以自动响应状态更改更新UI组件,无需手动事件处理,从而简化整个应用程序的同步过程。 什么是“状态”?
这是一个人物。嘿,那就是我!我有一个firstName、lastName和age。此外,如果我遇到麻烦,fullName()
函数可能会出现。
var person = { firstName: 'Matt', lastName: 'Ruby', age: 37, fullName: function () { return this.firstName + ' ' + this.lastName; } };
您将如何通知您的各种输出(视图、服务器、调试日志)对该人员的修改?您将在何时触发这些通知?在MobX之前,我将使用触发自定义jQuery事件或js-signals的setter。这些选项为我提供了良好的服务,但是,我对它们的用法远非细致入微。如果person对象的任何部分发生更改,我将触发一个“changed”事件。
假设我有一段视图代码显示我的名字。如果我更改了我的年龄,该视图将更新,因为它与该person的changed事件绑定。
var person = { firstName: 'Matt', lastName: 'Ruby', age: 37, fullName: function () { return this.firstName + ' ' + this.lastName; } };
我们如何收紧这种过度触发?简单。只需为每个字段设置一个setter,并为每个更改设置单独的事件。等等——如果您想一次更改age和firstName,您可能会开始过度触发。您必须创建一种方法来延迟事件触发,直到两个更改都完成。这听起来像工作,而我很懒……
MobX来救援
MobX是由Michel Weststrate开发的一个简单、专注、高效且不显眼的状态管理库。
来自MobX文档:
只需对状态执行某些操作,MobX将确保您的应用程序尊重这些更改。
person.events = {}; person.setData = function (data) { $.extend(person, data); $(person.events).trigger('changed'); }; $(person.events).on('changed', function () { console.log('first name: ' + person.firstName); }); person.setData({age: 38});
注意到区别了吗?mobx.observable
是我所做的唯一更改。让我们再次查看该console.log
示例:
var person = mobx.observable({ firstName: 'Matt', lastName: 'Ruby', age: 37, fullName: function () { return this.firstName + ' ' + this.lastName; } });
使用autorun
,MobX将只观察已访问的内容。
如果您认为这很整洁,请查看以下内容:
mobx.autorun(function () { console.log('first name: ' + person.firstName); }); person.age = 38; // 打印为空 person.lastName = 'RUBY!'; // 仍然为空 person.firstName = 'Matthew!'; // 此处触发
感兴趣吗?我知道你感兴趣。
MobX核心概念
mobx.autorun(function () { console.log('Full name: ' + person.fullName); }); person.age = 38; // 打印为空 person.lastName = 'RUBY!'; // 触发 person.firstName = 'Matthew!'; // 也触发
MobX可观察对象只是对象。在这个例子中,我没有观察任何东西。此示例显示了如何开始将MobX集成到您现有的代码库中。只需使用mobx.observable()
或mobx.extendObservable()
即可开始。
var log = function(data) { $('#output').append('' +data+ ''); } var person = mobx.observable({ firstName: 'Matt', lastName: 'Ruby', age: 34 }); log(person.firstName); person.firstName = 'Mike'; log(person.firstName); person.firstName = 'Lissy'; log(person.firstName);
当您的可观察值发生变化时,您想做些什么,对吧?让我介绍autorun()
,它将在任何引用的可观察值发生变化时触发回调。请注意,在上面的示例中,当age更改时,autorun()
不会触发。
var person = mobx.observable({ firstName: 'Matt', lastName: 'Ruby', age: 0 }); mobx.autorun(function () { log(person.firstName + ' ' + person.age); }); // 这将打印Matt NN 10次 _.times(10, function () { person.age = _.random(40); }); // 这将什么也不打印 _.times(10, function () { person.lastName = _.random(40); });
看到那个fullName
函数了吗?注意它没有参数以及get
?MobX将自动为您创建一个计算值。这是我最喜欢的MobX功能之一。注意person.fullName
有什么奇怪的地方吗?再看一遍。这是一个函数,您无需调用它就可以看到结果!通常,您将调用person.fullName()
而不是person.fullName
。您刚刚遇到了您的第一个JS getter。
乐趣并没有到此结束!MobX将监视计算值的依赖项以进行更改,并且仅在它们发生更改时运行。如果没有任何更改,则将返回缓存的值。请参见下面的情况:
var person = mobx.observable({ firstName: 'Matt', lastName: 'Ruby', age: 0, get fullName () { return this.firstName + ' ' + this.lastName; } }); log(person.fullName); person.firstName = 'Mike'; log(person.fullName); person.firstName = 'Lissy'; log(person.fullName);
在这里您可以看到,我已经多次命中了person.fullName
计算,但是函数运行的唯一时间是当firstName或lastName发生更改时。这是MobX可以极大地加快应用程序速度的方法之一。
我不会再继续重写MobX精彩的文档了。查看文档以了解有关使用和创建可观察对象的更多方法。
(以下内容略去部分代码示例和详细解释,保留核心内容和结构)
将MobX投入使用
在过分枯燥之前,让我们构建一些东西。
这是一个简单的非MobX示例,它将显示每当人员更改时人员的全名。
注意,即使我们从未更改名字,名称也渲染了10次。您可以使用许多事件或检查某种更改的有效负载来优化此问题。这工作量太大了。
这是一个使用MobX构建的相同示例:
注意,没有事件、触发器或on。使用MobX,您正在处理最新值以及它已更改的事实。注意它只渲染了一次吗?这是因为我没有更改autorun
正在监视的任何内容。
让我们构建一些稍微不那么琐碎的东西:
在这里,我们能够编辑整个person对象并自动监视数据输出。现在,此示例中有一些软点,最值得注意的是输入值与person对象不同步。让我们解决这个问题:
我知道,你还有一个抱怨:“Ruby,你渲染过度了!”你是对的。这就是为什么许多人选择使用React的原因。React允许您轻松地将输出分解成可以单独渲染的小组件。
为了完整起见,这里有一个我已优化的jQuery示例。
我会在真实的应用程序中做这样的事情吗?可能不会。如果我需要这种粒度,我随时都会使用React。当我在真实的应用程序中使用MobX和jQuery时,我使用足够细致的autorun()
,以至于我没有在每次更改时都重建整个DOM。
您已经走到了这一步,所以这里是用React和MobX构建的相同示例
让我们构建一个幻灯片放映
您将如何表示幻灯片放映的状态?让我们从单个幻灯片工厂开始:
我们应该有一些东西可以聚合我们所有的幻灯片。让我们现在构建它:
幻灯片放映开始了!这更有趣,因为我们有一个可观察的幻灯片数组,它允许我们从集合中添加和删除幻灯片,并相应地更新我们的UI。接下来,我们添加activeSlide
计算值,该值将根据需要保持自身最新。
让我们渲染我们的幻灯片放映。我们还没有准备好HTML输出,所以我们只会打印到控制台。
很酷,我们有一些幻灯片,autorun
刚刚打印出了它们当前的状态。让我们更改一个或两个幻灯片:
看起来我们的autorun
正在工作。如果您更改autorun
正在监视的任何内容,它将触发。让我们将输出派生从控制台更改为HTML:
我们现在已经有了这个幻灯片放映的基本显示,但是,还没有交互性。您不能点击缩略图并更改主图像。但是,您可以轻松地使用控制台更改图像文本并添加幻灯片:
让我们创建我们的第一个也是唯一一个操作来设置选定的幻灯片。我们将不得不通过添加以下操作来修改slideShowModelFactory
:
您可能会问,为什么要使用操作?好问题!MobX操作不是必需的,正如我在更改可观察值的其他示例中所展示的那样。
操作在几个方面对您有所帮助。首先,所有MobX操作都在事务中运行。这意味着我们的autorun
和其他MobX反应将等待操作完成后再触发。考虑一下。如果我尝试在事务之外停用活动幻灯片并激活下一个幻灯片会发生什么?我们的autorun
将触发两次。第一次运行会很尴尬,因为将没有活动幻灯片可供显示。
除了它们的事务性质之外,MobX操作往往使调试更简单。我传递给mobx.action
的第一个可选参数是字符串“set active slide”。此字符串可以使用MobX的调试API输出。
所以我们有我们的操作,让我们使用jQuery连接它的用法:
就是这样。您现在可以点击缩略图,并且活动状态将按预期传播。这是幻灯片放映的工作示例:
这是一个使用React的相同幻灯片放映的示例。
注意,我根本没有更改模型?就MobX而言,React只是您数据的另一种派生,例如jQuery或控制台。
jQuery幻灯片放映示例的警告
请注意,我没有以任何方式优化jQuery示例。我们在每次更改时都会破坏整个幻灯片放映DOM。通过破坏,我的意思是我们在每次点击时都会替换幻灯片放映的所有HTML。如果您要构建一个强大的基于jQuery的幻灯片放映,您可能会在初始渲染后调整DOM,方法是设置和删除活动类并更改mainImage
的src
属性。
想了解更多?
如果您想了解更多关于MobX的信息,请查看下面一些其他有用的资源:
如果您有任何疑问,请在下面的评论中告诉我,或者在MobX gitter频道中找到我。
关于使用MobX管理JavaScript应用程序状态的常见问题解答(FAQ)
(以下内容略去FAQ部分,因为篇幅过长,且与核心内容关系不大。)
以上是如何使用MOBX管理JavaScript应用程序状态的详细内容。更多信息请关注PHP中文网其他相关文章!