这篇文章是对Xstate的介绍,因为它可以在Svelte项目中使用。 Xstate在JavaScript生态系统中是独一无二的。它不会使您的DOM与应用程序状态同步,但是它可以通过允许您将其建模为有限状态机(FSM)来帮助管理您的应用程序状态。
深入了解国家机器和形式语言的范围超出了这篇文章的范围,但乔恩·贝拉(Jon Bellah)在另一篇CSS-Tricks文章中做到了这一点。现在,将FSM视为流程图。流程图具有多种状态,表示为气泡,并且从一个状态到下一个状态的箭头表示从一个状态到下一个状态的过渡。国家机器可以拥有一个以上的箭头,如果是最终状态,则根本没有一个箭头,甚至可以使箭头离开状态,并立即指向同一状态。
如果所有这些听起来都不知所措,那么放松,我们将详细介绍所有细节,又慢。目前,高级视图是,当我们将应用程序建模为状态机器时,我们将创建不同的“状态”,我们的应用程序可以进入(获取它…状态机……状态……状态?),发生并导致状态更改的事件将是这些状态之间的箭头。 XSTATE称这些国家为“状态”,而各州之间的箭头为“行动”。
Xstate具有学习曲线,这使教学具有挑战性。过于人为的用例,它看起来将是不必要的复杂。只有当应用程序的代码有些纠结时,Xstate才会发光。这使得它很棘手。话虽如此,我们将要查看的示例是一个自动完成的小部件(有时称为AutoSuggest),或一个输入框,当单击时,它会揭示一个要选择的项目列表,即在输入中输入时过滤器。
在这篇文章中,我们将考虑清理动画代码。这是起点:
这是我苗条的馆库中的实际代码,尽管此帖子已删除了不必要的作品。您可以单击输入并过滤项目,但是您将无法选择任何内容,通过项目,悬停等。我删除了与此帖子无关的所有代码。
我们将查看项目列表的动画。当您单击输入时,结果列表首先渲染时,我们希望将其放下动画。当您键入和过滤时,对列表的尺寸的更改将使越来越小的动画。当输入失去焦点时,或者您单击ESC时,我们将列表的高度动画为零,同时将其褪色,然后将其从DOM(而不是之前)删除。为了使事情变得更有趣(对用户来说很好),让我们使用与我们用于关闭的弹簧配置不同的弹簧配置,因此列表更快或僵硬地关闭了一点,因此不需要的UX不会在屏幕上徘徊太久。
如果您想知道为什么我不使用Svelte Transition来管理DOM内外动画,那是因为我还为列表打开时的尺寸,作为用户过滤时,在过渡之间进行协调,而常规的弹簧动画在从DOM中删除元素之前要恢复元素要零要困难得多。例如,如果用户快速键入并过滤列表,则会发生什么?正如我们将看到的那样,Xstate可以轻松地进行棘手的状态过渡。
到目前为止,让我们看一下示例的代码。当列表打开时,我们有一个开放变量可以控制,以及一个可控制列表是否应在DOM中的属性。我们还拥有一个闭合变量,可以控制列表是否在关闭过程中。
在第28行上,当输入单击或集中时,有一个输入式方法。现在,让我们注意,它设置为“ true”和“结果”。当用户在输入中键入输入并将其设置为true时,请调用输入变化。这是为了集中输入时,用户单击逃脱以关闭它,但然后开始键入,以便重新打开。而且,当然,当您期望时,输入布雷德函数会运行,并设置为true并开放为false。
让我们挑选出这种纠结的混乱,看看动画的工作原理。请注意顶部的slideinspring和不透明度。前者上下滑动列表,并将大小调整为用户类型。隐藏时,后者逐渐消失。我们将主要关注Slideinspring。
看看称为setSpringDimensions的函数的怪异。这更新了我们的幻灯片弹簧。为了关注重要作品,我们采用了一些布尔属性。如果列表正在打开,我们设置了开放式弹簧配置,我们立即通过{Hard:true}配置来设置列表的宽度(我希望列表仅滑下而不是向下滑动),然后设置高度。如果我们要关闭,我们将动画至零,并且,当动画完成时,我们将resuctlistlistvissvissvission设置为false(如果关闭动画被中断,那么Svelte将足够聪明,以至于无法解决承诺,以免回调)。最后,此方法也随时称为结果列表的大小更改,即作为用户过滤。我们在其他地方设置了一个调节器来管理此操作。
让我们盘点此代码。
你抓了这个错误吗?按下ESC按钮时,我只设置为false。我忘了设置为true,并调用setSpringDimensions(false,true)。此博客文章没有故意人造这个错误!这是我对这个小部件动画进行大修时犯的实际错误。我可以将输入中的代码粘贴到捕获逃生按钮的位置,甚至将其移至新功能并从两个地方调用它。该错误在根本上并不难解决,但确实会增加代码的认知负载。
我们要跟踪很多事情,但是最糟糕的是,这种状态散布在整个模块中。采用上面描述的任何状态,并使用Codesandbox的查找功能来查看使用该状态的所有位置。您会看到光标在文件上弹跳。现在,想象一下您是该代码的新手,试图理解它。考虑一下您必须跟踪所有这些状态作品的心理模型的日益增长的心理模型,根据其存在的所有位置来弄清楚它的工作原理。我们都去过那里;很烂。 Xstate提供了更好的方法;让我们看看如何。
让我们退后一步。在用户互动时发生的事件发生,导致副作用以及向新状态的过渡时,事件发生的事件发生,对我们的小部件进行建模是否会更简单?当然,但这就是我们已经在做的事情;问题是,代码无处不在。 Xstate使我们能够以这种方式正确建模我们的状态。
不要指望Xstate神奇地使我们的所有复杂性消失。我们仍然需要协调我们的弹簧,根据打开和关闭状态调整弹簧配置,句柄尺寸等。XSTATE给我们的是能够以易于推理和调整的方式集中该状态管理代码。实际上,由于我们的状态机器设置,我们的总体数量将有所增加。让我们看看。
让我们直接跳入,看看裸露的骨骼状态机的外观。我正在使用XSTATE的FSM软件包,这是一个最小的XSTATE版本,具有很小的1KB捆绑包大小,非常适合图书馆(例如AutoSoggest Widget)。它没有太多的高级功能,例如完整的Xstate软件包,但是我们不需要它们来使用它们,并且我们不希望它们像这样的介绍性帖子。
我们的状态机器的代码在下面,交互式演示已在代码沙箱中结束。有很多,但是我们很快就会解决。明确的是,它尚不正常。
const statemachine = createmachine( { 初始:“初始”, 语境: { 开放:false, 节点:null },, 状态:{ 最初的: { ON:{open:“打开”} },, 打开: { 在: { 渲染:{操作:“渲染”}, 调整大小:{action:“ ressize”}, 关闭:“关闭” },, 条目:“打开” },, 关闭:{ 在: { 打开:{target:“打开”,操作:[“ ressize”]}, 关闭:“关闭” },, 条目:“关闭” },, 关闭:{ 在: { 开放:“打开” },, 条目:“关闭” } } },, { 动作:{ 打开:分配(context => { 返回{...上下文,打开:true}; }), 渲染:分配(((context,evt)=> { const {node} = evt; 返回{... context,node}; }), 关闭() {}, 调整大小(上下文){}, 闭合:nistion(()=> { 返回{open:false,node:null}; })) } } );
让我们从上到下。初始属性控制了初始状态,我称之为“初始”。上下文是与我们的状态机关联的数据。我正在为结果列表当前是否打开,以及同一结果列表的节点对象。接下来,我们看到我们的州。每个状态都是状态财产中的关键。对于大多数州,您可以看到我们有一个属性和入口属性。
在配置事件上。对于每个事件,我们都可以过渡到新状态;我们可以运行副作用,称为动作;或两者兼而有之。例如,当开放事件发生在初始状态的内部时,我们将进入开放状态。当渲染事件发生在开放状态时,我们会执行渲染的动作。当开放事件发生在闭合状态内时,我们将过渡到空旷状态,并运行调整大小操作。您在大多数状态下看到的输入字段将在输入状态时自动运行的操作。尽管我们在这里不需要它们,但也有退出操作。
我们还有更多的东西要涵盖。让我们看一下状态机器的数据或上下文如何改变。当我们希望采取行动修改上下文时,我们将其包裹在分配中并从我们的行动中返回新上下文;如果我们不需要任何处理,我们可以直接通过新状态进行分配。如果我们的操作没有更新上下文,即,它只是用于副作用,那么我们不会将动作功能包裹在分配中,而只是执行所需的任何副作用。
我们为我们的状态机有一个很酷的型号,但是我们如何运行呢?我们使用解释功能。
const statemachineservice =解释(statemachine).start();
现在,statemachineservice是我们的运行状态机,我们可以在该机器上调用事件以强迫我们的过渡和行动。要解雇事件,我们致电发送,传递事件名称,然后选择事件对象。例如,在结果列表首先安装在DOM中时运行的Svelte动作中,我们有:
statemachineservice.send({type:“ rendered”,node});
这就是渲染操作获得结果列表的节点的方式。如果您查看其余的autocomplete.svelte文件,您将看到所有供电的状态管理代码替换为单线事件调度。如果输入单击/聚焦的处理程序,我们将运行“开放事件”。我们的resizeObserver发射了调整大小事件。等等。
让我们暂停一下,欣赏Xstate在这里免费给我们的东西。让我们看一下在添加Xstate之前单击或聚焦输入时运行的处理程序。
函数inputEnged(evt){ 如果(关闭){ setSpringDimensions(); } open = true; Results ListVisible = true; }
之前,我们正在检查是否要关闭,如果是这样,则迫使我们的滑动弹簧重新计算。否则我们打开了小部件。但是,如果我们已经打开输入时单击输入,会发生什么呢?相同的代码re-Ran。幸运的是,这并不重要。 Svelte不在乎我们是否重新设置开放,并符合他们已经持有的价值观。但是这些担忧随Xstate消失了。新版本看起来像这样:
函数inputEnged(evt){ statemachineservice.send(“ open”); }
如果我们的状态计算机已经处于公开状态,并且我们开了开放事件,那么什么也不会发生,因为没有为该状态配置的开放事件。当结果关闭时单击输入的特殊处理?这也是在状态计算机配置中直接处理的 - 请注意,从闭合状态运行时,开放式事件在调整大小操作上如何进行。
而且,当然,我们已经从前修复了ESC密钥错误。现在,按键只需发射关闭事件,就是这样。
结局几乎是反高潮的。我们需要采取以前从事的所有工作,然后将其移至我们的行动中的正确位置。 XSTATE不会消除我们编写代码的需求;它仅提供一个结构化的,清晰的地方。
{ 动作:{ 打开:分配({open:true}), 渲染:分配(((context,evt)=> { const {node} = evt; const dimensions = getResultSlistDimensions(node); itemSheightObserver.Observe(node); opacityspring.set(1,{hard:true}); object.Assign(slideinspring,slide_open); slideinspring.update(prev =>({... prev,width:dimensions.width}),{ 硬:是的 }); slideinspring.set(dimensions,{hard:false}); 返回{... context,node}; }), 关闭() { opacityspring.set(0); object.Assign(slideinspring,slide_close); slideinspring .update(prev =>({... prev,高度:0})) 。然后(()=> { statemachineservice.send(“关闭”); }); },, 调整大小(上下文){ opacityspring.set(1); slideinspring.set(getResultSlistDimensions(context.node)); },, 闭合:nistion(()=> { itemSheightObserver.unobserve(resultList); 返回{open:false,node:null}; })) } }
我们的动画状态在我们的状态机器中,但是我们如何将其清除呢?我们需要开放状态来控制我们的结果列表渲染,尽管在此演示中未使用,但此AutoSuggest Widget的真实版本需要结果列表dom节点,例如将当前突出显示的项目滚动到视图中。
事实证明,我们的statemachineservice具有订阅方法,每当发生状态变化时就会发射。通过当前状态机状态调用您通过的回调,其中包括上下文对象。但是Svelte的袖子有一个特殊的技巧:其反应性语法为$:不仅可以与组件变量和Svelte商店一起使用;它还可以使用订阅方法与任何对象一起使用。这意味着我们可以与我们的状态机同步,这样简单:
$:({open,node:resuiltList} = $ statemachineservice.context);
只是常规的破坏,有一些帕伦斯(Parens)可以帮助事物正确解析。
这里有一个快速注意,作为改进的领域。目前,我们有一些既执行副作用又更新状态的动作。理想情况下,我们可能应该将它们分为两个动作,一个仅用于副作用,另一个使用新状态分配。但是,我决定让本文尽可能简单,以帮助减轻Xstate的引入,即使一些事情不太理想。
我希望这篇文章对Xstate产生了兴趣。我发现它是一种非常有用,易于使用的工具来管理复杂状态。请知道我们只刮擦了表面。我们专注于最小的FSM软件包,但是整个Xstate库的能力比我们在这里涵盖的内容(从嵌套状态到对承诺的一流的支持)的功能要多得多,甚至具有状态可视化工具!我敦促您检查一下。
愉快的编码!
以上是与Xstate协调Svelte动画的详细内容。更多信息请关注PHP中文网其他相关文章!