web-dev-qa-db-fra.com

Intégrez SignalR avec Vue.js et Vuex

Nous travaillons actuellement sur une application de vote basée sur le modèle de webpack vue-cli. Puisque nous voulons stocker et manipuler notre état de vote d'une manière cohérente et maintenable, nous avons l'intention d'utiliser vuex pour la gestion de l'état. L'interaction entre le frontend et le backend est basée sur les websockets et nous voulons utiliser signalr car il s'est avéré très bon dans les projets précédents. Étant donné que nous sommes nouveaux sur vue.js, nous avons besoin de conseils pour intégrer parfaitement signalr, vuex et vue.js.

Décrivons le scénario:

Le frontend reçoit un événement de notre backend pour reconnaître que le sondage est actif et peut recevoir les réponses sélectionnées. Après un certain temps, nous informons le frontend que les résultats sont disponibles et les affichons à l'utilisateur. Dans certains cas, nous pourrions ouvrir un autre sondage. Il est important que nous puissions nous déconnecter au cas où le document serait masqué (API de visibilité de la page).

Notre approche de solution:

En général, j'aimerais implémenter un signal.service spécial à cet effet. Ce service est chargé d'établir la connexion ainsi que d'envoyer et de recevoir les messages via des websockets. Comme nous ne sommes pas en mesure d'effectuer des modifications dans le magasin vuex à partir d'un module commun, nous avons compris qu'un plugin vuex conviendrait. Le plugin vuex devrait encapsuler signalr.

Dans le cas où nous recevons un VotingStartEvent nous déverrouillerions la question respective et l'afficherions à l'utilisateur. Si l'utilisateur a répondu à cette question, nous soumettons le nouvel état de cette question (réponse) au magasin vuex. Dans notre plugin, nous avons un abonnement aux mutations et nous utiliserions cet abonnement pour envoyer notre vote au backend. L'extrait suivant illustre l'idée:

var plugin = (store) => {
  // ...
  store.subscribe((mutation, state) => {
    console.log('inside subscription');
    // if vote has answered state, call connection.send() to submit the message to the backend
  });

  connection.received((message) => {
    // ...
    var event = parseEvent(message);
    if (event.type === START) {
      store.commit({type: 'voting', item: 'unlocked'});
    }
    // ...
  });
}

Cette approche est-elle bonne ou voyez-vous une marge d'amélioration?

10
jimmy

Il n'y a pas besoin de plugins ou de constructions comme Vue.prototype.$pusher = new Pusher('apiKey'). Je tiens l'instance de Pusher comme toute autre valeur dont j'ai besoin à partager entre les composants. Et j'initialise le Pusher dans Vue méthode d'instance create, comme toutes les autres bibliothèques que j'ai besoin d'initialiser en premier. Pour plus de précision, je conserve délibérément les données commerciales dans les instances de composants, comme ils sont uniques pour chaque instance. Pas tout ce dont vous avez besoin, vous devez le placer uniquement pour le stockage.

var store = new Vuex.Store({
  state: {
    pusher: null
  },
  mutations: {
    saveInstance(state, instance) {
      state.pusher = instance
    }
  },
  actions: {
    initializePusher ({commit}, apiKey) {
      commit('saveInstance', new Pusher(apiKey))
    }
  }
})

Vue.component('live-price', {
  template: '#live-price',
  props: ['pair'],
  data () {
    return {
      price: 'wait for next trade...',
      channel: null
    }
  },
  created () {
    this.channel = this.$store.state.pusher.subscribe('live_trades_' + this.pair)
    this.channel.bind('trade', data => this.price = data.price)
  }
})

new Vue({
  el: '#app',
  store,
  created () {
    this.$store.dispatch('initializePusher', 'de504dc5763aeef9ff52')
  }
})
[v-cloak] { display: none }
<div id="app">
  <live-price pair="btceur">BITCOIN price in EUR:</live-price>
  <live-price pair="ltceur">LITECOIN price in EUR:</live-price>
  <live-price pair="etheur">ETHEREUM price in EUR:</live-price>
  <live-price pair="xrpeur">RIPPLE price in EUR:</live-price>
</div>

<template id="live-price">
  <p>
    <slot></slot>
    <span v-cloak>{{ price }}</span>
  </p>
</template>

<script src="https://unpkg.com/[email protected]/dist/vue.min.js"></script>
<script src="https://unpkg.com/[email protected]/dist/vuex.min.js"></script>
<script src="https://js.pusher.com/4.1/pusher.min.js"></script>
5
user6748331