大規模なシングルページアプリの本格的な開発を行ったことがある場合は、状態管理の概念、特にFacebookがReduxを通じて普及しているFluxアーキテクチャに精通していると思います。 ここでは、Vuexを使用してVueアプリケーションにこのようなパターンを実装する方法を示します。

準備

インストール

Vuex は、Vueの公式パッケージですが、組み込みではありません。 アプリで必要かどうかを判断し、ヤーンまたはnpmを使用してインストールする必要があります。

# Yarn
$ yarn add vuex
# NPM
$ npm install vuex --save

次に、アプリのブートストラップで、Vuexプラグインが有効になっていることを確認します。

main.js
import Vue from 'vue';
import Vuex from 'vuex';
import App from 'App.vue';

Vue.use(Vuex);

new Vue({
  el: '#app',
  render: h => h(App)
});

ストアの作成

Vuexが提供する機能を利用できるようにするには、最初にストアを作成する必要があります。 ストアは基本的に、通常のVue反応性パターンに従うグローバルな反応性オブジェクトです。 一貫した状態を確保し、変更を簡単に追跡できるようにするために、直接アクセスまたは変更することはできません。 作成方法は次のとおりです。

store.js
export const store = new Vuex.Store({
  state: {
    safelyStoredNumber: 0
  }
});

ここで、ストアにアクセスするには、すべてのコンポーネントにインポートするか、ルートVueインスタンスに挿入して、アプリ内の他のすべてのコンポーネントにthis。$storeとして自動的に挿入する必要があります。 。 この記事の残りの部分では、後者の方法を使用します。

main.js
import Vue from 'vue';
import Vuex from 'vuex';
import App from 'App.vue';
import { store } from './store.js';

Vue.use(Vuex);

new Vue({
  store,
  el: '#app',
  render: h => h(App)
});

アクセス状態

今のところ、あなたはその店で本当に何もすることができません。 これは、状態の分離されたブラックボックスであり、予期しないアクションによる読み取りや操作を防ぎます。 ストアからデータを読み取るには、getterを作成する必要があります。

ゲッターの使用

ゲッターは、状態オブジェクトを受け取り、そこから値を返すストア内の関数です。 コンポーネントでは、this。$store.getters.property を介して、関数ではなく計算プロパティとしてアクセスできます。 ゲッターがパラメーターを必要とする場合、パラメーターを受け取る2番目の関数を返すことができます。

store.js
export const store = new Vuex.Store({
  state: {
    safelyStoredNumber: 0
  },
  getters: {
    safelyStoredNumber: state => state.safelyStoredNumber,
    storedNumberMatches(state) {
      return matchNumber => {
          return state.safelyStoredNumber === matchNumber;
      }
    }
    // Shorthand:
    // storedNumberMatches: state => matchNumber => state.safelyStoredNumbers === matchNumber
  }
});

ただし、コンポーネント内のゲッターにアクセスする簡単な方法は、VuexのmapGettersヘルパーメソッドを使用することです。 これにより、コンポーネントの最上位の計算済みプロパティにゲッターをマウントできます。

コンポーネントのゲッターの名前を変更する場合は、mapGettersでオブジェクトを取得できます。

App.vue
<template>
  <p>The safely stored number: {{safelyStoredNumber}}<p>
</template>

<script>
import { mapGetters } from 'vuex'

export default {
  computed: {
    ...mapGetters([
      // Mounts the "safelyStoredNumber" getter to the scope of your component.
      'safelyStoredNumber'
    ])
  }
}
</script>

状態の変更

同期変異

Vuex の状態を直接変更するには、mutationという関数を呼び出します。 ミューテーションには、現在の状態とオプションのペイロードが渡されます。 ペイロードは任意のオブジェクトにすることができます。 Mutations は同期している必要があり、値を返さないようにする必要があります。 this。$store.commit(’mutationName’、payload)を実行することで直接使用できます。

store.js
export const store = new Vuex.Store({
  state: {
    safelyStoredNumber: 0
  },
  ...
  mutations: {
    incrementStoredNumber(state) {
      state.safelyStoredNumber++;
    },
    setStoredNumber(state, newNumber) {
      // newNumber is the payload passed in.
      state.safelyStoredNumber = newNumber;
    }
  }
});

ゲッターと同様に、 Vuex には、コンポーネント内の mutations の便利なメソッド、mapMutationsヘルパーメソッドがあります。 これにより、コンポーネントのメソッドとしてミューテーションをマウントできます。

App.vue
<template>
  <p>The safely stored number: {{safelyStoredNumber}}<p>
</template>

<script>
import { mapMutations } from 'vuex'

export default {
  ...
  methods: {
    ...mapMutations([
      // Mounts the "incrementStoredNumber" mutation to `this.incrementStoredNumber()`.
      'incrementStoredNumber',
      // Mounts the "setStoredNumber" mutation to `this.setStoredNumber(newNumber)`.
      'setStoredNumber'
    ])
  }
}
</script>

非同期アクション

より複雑なアプリでは、状態を変更する非同期アクションを実行する必要がある可能性があります。 Vuexはこれをactionsで処理します。 これらは状態オブジェクトでも定義され、状態コンテキスト全体が渡されます。これにより、ゲッターにアクセスしてミューテーションをコミットできます。 完了ステータスを示すpromiseを返すことが期待されます(必須ではありません)。 ES2017 async / await を使用すると、非常に簡潔でわかりやすい非同期アクションを記述できます。 アクションは、this。$store.dispatch(’actionName’、payload).then(response => {})を使用してコンポーネントで直接使用されます。

アクション内の状態を変更するには、 context.commit(’mutationName’、payload)を使用します。 アクション内で複数のミューテーションが許可されます。

store.js
import myRemoteService from './my-remote-service.js'

export const store = new Vuex.Store({
  state: {
    safelyStoredNumber: 0
  },
  ...
  actions: {
    async setNumberToRemoteValue(context) {
      // Commits the 'setStoredNumber' mutation with the value of whatever myRemoteService.getRemoteValue() resolves through a promise.
      context.commit('setStoredNumber', await myRemoteService.getRemoteValue());
      return Promise.resolve();
    },
  }
});

async / await に慣れていない場合は、真剣に読んでください。 それは素晴らしいです。 つまり、 awaited promiseが解決されるまで現在の関数の実行を一時停止し、通常は余分な定型文を必要とせずに、基本的にpromise解決を変数として使用できるようにします。

アクションVuexコンビニエンスメソッド(予想どおり mapActions という名前)は、ミューテーションの場合と同じ方法で使用されます。

App.vue
<template>
  <p>The safely stored number: {{safelyStoredNumber}}<p>
</template>

<script>
import { mapActions } from 'vuex'

export default {
  ...
  methods: {
    ...mapActions([
      // Mounts the "setNumberToRemoteValue" action to `this.setNumberToRemoteValue()`.
      'setNumberToRemoteValue',
    ])
  }
}
</script>

モジュール化

少数のデータセットのみを操作する場合は単一のストアで問題ありませんが、必然的に、ある時点で、増え続けるアクション、ミューテーション、およびゲッターのリストを別々のセクションに分割する必要があります。 ありがたいことに、Vuexはこれを行うためのシステムも提供します。 モジュール。 恐ろしい名前にもかかわらず、モジュールは状態ゲッターミューテーション、およびアクションプロパティを持つ単なる通常のオブジェクトです。 これを行うことで簡単に作成できます。

my-store-module.js
export const myModule = {
  // This makes your getters, mutations, and actions accessed by, eg: 'myModule/myModularizedNumber' instead of mounting getters, mutations, and actions to the root namespace.
  namespaced: true,
  state: {
    myModularizedNumber: 0
  },
  getters: {
    myModularizedNumber: state => state.myModularizedNumber
  },
  mutations: {
    setModularizedNumber(state, newNumber) {
      state.myModularizedNumber = newNumber
    }
  }
}
store.js
import { myModule } from './my-store-module.js';

export const store = new Vuex.Store({
  modules: {
      myModule
  },
  state: {
    safelyStoredNumber: 0
  },
  ...
});

モジュールは、好きなだけモジュールにネストできます。 さらに、 mapGetters、mapMutations、およびmapActions はすべて、次のようなコードを記述しなくても済むように、モジュールの名前空間である最初の引数を取ることができます。

...mapGetters([
  'myModule/nestedModule/subNestedModule/exampleGetter',
  'myModule/nestedModule/subNestedModule/anotherGetter',
])

代わりに、次のように書くことができます。

...mapGetters('myModule/testedModule/subNestedModule', [
  'exampleGetter',
  'anotherGetter'
])

Vuex を使用して状態管理をクリアするのに役立つことを願っています!