对VueJS使用XSTATE





David Khourshid 使用XState来声明性地描述VueJS 2组件的逻辑的一个小例子XState是一个非常高级的库,用于在JS中创建和使用状态机。在创建Web应用程序这一艰巨的任务中,这不是一个坏帮助。



史前史



在上一篇文章中,我简要介绍了为什么需要状态机(状态机)以及使用Vue的简单实现。我的自行车只有状态,并且状态声明如下:



{
    idle: ['waitingConfirmation'],
    waitingConfirmation: ['idle','waitingData'],
    waitingData: ['dataReady', 'dataProblem'],
    dataReady: [‘idle’],
    dataProblem: ['idle']
}


实际上,这是状态的枚举,并且针对每个状态描述了系统可以进入的一系列可能状态。该应用程序只是简单地对状态机“说”-我想进入这种状态,如果可能的话,该机器会进入所需的状态。



此方法有效,但有一些不便之处。例如,如果处于不同状态的按钮应启动到不同状态的转换。在这种情况下,我们必须围栏。与其说声明性,不如说是一团糟。



在研究了有关YouTube视频的理论之后,很明显事件是必要且重要的。这种宣言诞生于我的脑海:



{
  idle: {
    GET: 'waitingConfirmation',
  },
  waitingConfirmation: {
    CANCEL: 'idle',
    CONFIRM: 'waitingData'
  },
  waitingData: {
    SUCCESS: 'dataReady',
    FAILURE: 'dataProblem'
  },
  dataReady: {
    REPEAT: 'idle'
  },
  dataProblem: {
    REPEAT: 'idle'
  }
}


这已经与XState库描述状态的方式非常相似。在更仔细地阅读了码头之后,我决定将我的自制自行车放在谷仓中,然后换成有品牌的自行车。



VUE + XState



安装非常简单,请阅读文档,安装后,我们在组件中包含XState:



import {Machine, interpret} from ‘xstate’


我们基于声明对象创建汽车:



const myMachine = Machine({
    id: 'myMachineID',
    context: {
      /* some data */
    },
    initial: 'idle',
    states: {
        idle: {
          on: {
            GET: 'waitingConfirmation',
          }
        },
        waitingConfirmation: {
          on: {
            CANCEL: 'idle',
            CONFIRM: 'waitingData'
          }
        },
        waitingData: {
          on: {
            SUCCESS: 'dataReady',
            FAILURE: 'dataProblem'
          },
        },
        dataReady: {
          on: {
            REPEAT: 'idle'
          }
        },
        dataProblem: {
          on: {
            REPEAT: 'idle'
          }
        }
    }
})


很明显,有状态'idle','waitingConfirmation'...以及大写的GET,CANCEL,CONFIRM ...中的事件。



机器本身不工作,您需要使用解释功能从中创建服务。我们将在我们的状态下放置一个指向该服务的链接,同时在当前状态下放置一个链接:



data: {
    toggleService: interpret(myMachine),
    current: myMachine.initialState,
}


该服务必须启动-start(),并且还指示在状态转换时,我们更新current的值:



mounted() {
    this.toggleService
        .onTransition(state => {
            this.current = state
         })
        .start();
    }


我们将send函数添加到方法中,并使用它来控制机器-向其发送事件:



methods: {
   send(event) {
      this.toggleService.send(event);
   },
} 


好吧,那么一切都很简单。只需调用以下命令即可发送事件:



this.send(‘SUCCESS’)


找出当前状态:



this.current.value


如下检查机器是否处于特定状态:



this.current.matches(‘waitingData')




放在一起:



模板
<div id="app">
  <h2>XState machine with Vue</h2>
  <div class="panel">
    <div v-if="current.matches('idle')">
      <button @click="send('GET')">
        <span>Get data</span>
      </button>
    </div>
    <div v-if="current.matches('waitingConfirmation')">
      <button @click="send('CANCEL')">
        <span>Cancel</span>
      </button>
      <button @click="getData">
        <span>Confirm get data</span>
      </button>
    </div>
    <div v-if="current.matches('waitingData')" class="blink_me">
      loading ...
    </div>
    <div v-if="current.matches('dataReady')">
      <div class='data-hoder'>
        {{ text }}
      </div>
      <div>
        <button @click="send('REPEAT')">
          <span>Back</span>
        </button>
      </div>
    </div>
    <div v-if="current.matches('dataProblem')">
      <div class='data-hoder'>
        Data error!
      </div>
      <div>
        <button @click="send('REPEAT')">
          <span>Back</span>
        </button>
      </div>
    </div>
  </div>
  <div class="state">
    Current state: <span class="state-value">{{ current.value }}</span>
  </div>
</div>




s
const { Machine, interpret } = XState

const myMachine = Machine({
    id: 'myMachineID',
    context: {
      /* some data */
    },
    initial: 'idle',
    states: {
        idle: {
          on: {
            GET: 'waitingConfirmation',
          }
        },
        waitingConfirmation: {
          on: {
            CANCEL: 'idle',
            CONFIRM: 'waitingData'
          }
        },
        waitingData: {
          on: {
            SUCCESS: 'dataReady',
            FAILURE: 'dataProblem'
          },
        },
        dataReady: {
          on: {
            REPEAT: 'idle'
          }
        },
        dataProblem: {
          on: {
            REPEAT: 'idle'
          }
        }
    }
	})



new Vue({
  el: "#app",
  data: {
  	text: '',
  	toggleService: interpret(myMachine),
    current: myMachine.initialState,
  },
  computed: {

  },
  mounted() {
    this.toggleService
        .onTransition(state => {
          this.current = state
        })
        .start();
  },
  methods: {
    send(event) {
      this.toggleService.send(event);
    },
    getData() {
      this.send('CONFIRM')
    	requestMock()
      .then((data) => {       
      	this.text = data.text   
      	this.send('SUCCESS')
      })
      .catch(() => this.send('FAILURE'))
    },

  }
})

function randomInteger(min, max) {
  let rand = min + Math.random() * (max + 1 - min)
  return Math.floor(rand);
}

function requestMock() {
  return new Promise((resolve, reject) => {
  	const randomValue = randomInteger(1,2)
  	if(randomValue === 2) {
    	let data = { text: 'Data received!!!'}
      setTimeout(resolve, 3000, data)
    }
    else {
    	setTimeout(reject, 3000)
    }
  })
}




当然,所有这些都可以在jsfiddle.net找到



可视化器



XState提供了一个很棒的工具Visualizer您可以看到特定汽车的图表。并且不仅要查看事件,还要单击事件并进行转换。这是我们的示例如下所示:







结果



XState可与VueJS完美结合。这简化了组件的工作,并让您摆脱了不必要的代码。最主要的是,机器的声明使您可以快速理解逻辑。这个例子很简单,但是我已经在一个正在运行的项目的更复杂的例子上进行了尝试。飞行正常。



在本文中,由于我仍然拥有足够的功能,因此仅使用了该库的最基本功能,但是该库包含许多更有趣的功能:



  • 保护过渡
  • 动作(进入,退出,过渡)
  • 扩展状态(上下文)
  • 正交(平行)状态
  • 分层(嵌套)状态
  • 历史


还有类似的库,例如Robot。这里是比较状态机比较:XState与。机器人因此,如果您对某个主题感兴趣,那么您将有事要做。



All Articles