用 XState 替换 Vuex
过去几个月我一直在学习 XState 和状态机,我非常喜欢它们。我决定用 Vue 和 XState 开发一个应用。我想和大家分享一下,因为我看到很多关于如何将 XState 集成到 React 中的帖子、视频、教程等等,但关于如何将其集成到 Vue 中的却不多。
一开始,我结合使用 Vuex 和 XState。我深受 Phillip Parker 的这篇文章以及他创建的这个GitHub 代码库的启发。如果你还没读过这篇文章并看过代码,我强烈建议你读一读,我从中受益匪浅。
基本上,应用程序的每个功能我都对应一个 Vuex 模块和一个 XState 状态机。虽然运行良好,但我知道我并没有充分利用 XState 的潜力。
经过进一步研究,我找到了一种完全摆脱 Vuex 的方法,同时还能利用 Vue 的各项功能实现全局响应式状态,并结合强大的有限状态机,从而实现 XState 的所有特性。这种方法更接近XState 文档中展示的Vue 实现方案。
我不用 Vuex,而是使用事件总线模式来管理全局状态。这意味着创建一个新的 Vue 实例,并将需要在组件间共享的任何内容传递给它。对于简单的应用,可能只需要一个实例,但大多数应用可能更适合拆分成多个模块(就像使用 Vuex 那样)。
然后,您只需将所需的 XState 机器数据传递给这个 Vue 实例即可。我编写了一个函数,该函数返回一个 Vue 实例,该实例暴露了机器的状态、上下文和send()方法,并对机器的变化做出反应。
import Vue from "vue";
import { interpret } from "xstate";
export const generateVueMachine = machine => {
return new Vue({
created() {
this.service
.onTransition(state => {
this.current = state;
this.context = state.context;
if (process.env.NODE_ENV === "development") {
console.log(`[ ${machine.id.toUpperCase()} STATE ]`, this.current.value);
}
})
.start();
},
data() {
return {
current: machine.initialState,
context: machine.context,
service: interpret(machine)
};
},
methods: {
send(event) {
this.service.send(event);
}
}
});
};
然后,您可以创建一个新文件,例如 fetchMachine.js,在其中创建一个 XState 状态机。您可以使用该generateVueMachine()函数并将您的状态机作为参数传递给它,该函数会返回一个您可以导出的 Vue 实例。
import { Machine, assign } from "xstate";
import { generateVueMachine } from "./generateVueMachine";
const machine = Machine({ /*...machine config */ });
export const fetchMachine = generateVueMachine(machine);
现在我可以在我的应用程序中的任何地方引用这台机器,并使用 Vue 的计算属性对其变化做出反应。
<template>
<button @click="onFetch" v-if="!fetchState.matches('fetching')">Fetch<button>
<p>{{ fetchContext.fetchResult }}</p>
</template>
<script>
// fsm
import { fetchMachine } from "./fsm/fetchMachine";
export default {
computed: {
fetchState() {
return fetchMachine.current;
},
fetchContext() {
return fetchMachine.context;
}
},
methods: {
onFetch() {
fetchMachine.send({type: 'FETCH'});
}
}
};
</script>
就这样。
这是我的应用程序仓库的链接,您可以查看我如何在实际环境中应用它(状态机文件位于client/fsm)。
我非常希望得到一些反馈,告诉我哪些方面可以改进。
编辑:
我在 npm 上创建了一个 Vue 插件,以简化此设置并消除一些样板代码。您可以在https://github.com/f-elix/vue-xstate-plugin找到它。