web-dev-qa-db-ja.com

SignalRをVue.jsおよびVuexと統合する

現在、vue-cli webpackテンプレートに基づいた投票アプリの開発に取り組んでいます。一貫性のある保守可能な方法で投票状態を保存および操作するため、状態管理にvuexを使用します。フロントエンドとバックエンドの相互作用はwebsocketに基づいており、以前のプロジェクトで非常に優れていることが証明されているため、signalrを使用します。 vue.jsが初めてなので、signalr、vuex、vue.jsを完全に統合する方法についてのアドバイスが必要です。

シナリオを説明しましょう:

フロントエンドは、投票ポーリングがアクティブであり、選択した回答を受信できることを認識するイベントをバックエンドから取得します。しばらくして、結果が利用可能であることをフロントエンドに通知し、ユーザーに表示します。場合によっては、別の投票投票を開くことがあります。ドキュメントが非表示になった場合(ページ表示API)に切断できるようにすることが重要です。

当社のソリューションアプローチ:

一般に、この目的のために特別なsignal.serviceを実装したいと思います。このサービスは、接続の確立とwebsocketを介したメッセージの送受信を担当します。共通のモジュールからvuexストアに変更を加えることができないため、vuexプラグインが適していることがわかりました。 vuexプラグインはシグナルをラップする必要があります。

VotingStartEventを受け取った場合、それぞれの質問のロックを解除してユーザーに表示します。ユーザーがその質問に回答した場合、この質問の新しい状態(回答済み)をvuexストアにコミットします。プラグインの内部には、ミューテーションのサブスクリプションがあり、このサブスクリプションを使用して投票をバックエンドに送信します。次のスニペットはそのアイデアを示しています。

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'});
    }
    // ...
  });
}

このアプローチは良いですか、改善の余地はありますか?

10
jimmy

プラグインやVue.prototype.$pusher = new Pusher('apiKey')のような構造は必要ありません。コンポーネント間で共有する必要がある他の値と同様に、Pusherインスタンスを保持します。そして、私はプッシャーをVueインスタンスメソッドcreateで初期化します。最初に初期化する必要がある他のライブラリと同様に。これらはインスタンスごとに一意です。

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