ストアディレクトリ
store
ディレクトリには Vuex ストアに関するファイルが含まれています。Vuex ストアは Nuxt に付属していてすぐに使えますが、デフォルトでは無効化されています。このディレクトリ内に index.js
ファイルを作成することで、ストアが有効化されます。
ストアを使って状態を管理するのは、どういった大きなアプリケーションを作るにあたっても重要なことです。それがなぜ Nuxt がそのコアに Vuex を組み込んでいるかの理由です。
ストアを有効化する
Nuxt は store
ディレクトリを確認します。もし隠しファイルや README.md
ではないファイルが含まれていたら、ストアは有効化されます。これは Nuxt が以下を行うことを意味しています:
- Vuex をインポートする
-
ルートの Vue インスタンスに
store
オプションを追加します。
モジュール
store
ディレクトリの中にあるすべての .js
ファイルは名前空間付きモジュール に変換されます(index
はルートモジュールになります)。state
の値は不必要に状態がサーバサイドで共有されてしまうことを避けるため、常に function
であるべきです。
はじめに、ステートを関数で、ミューテーションとアクションをオブジェクトでエクスポートしましょう。
export const state = () => ({
counter: 0
})
export const mutations = {
increment(state) {
state.counter++
}
}
次に、store/todos.js
ファイルを作成しましょう:
export const state = () => ({
list: []
})
export const mutations = {
add(state, text) {
state.list.push({
text,
done: false
})
},
remove(state, { todo }) {
state.list.splice(state.list.indexOf(todo), 1)
},
toggle(state, todo) {
todo.done = !todo.done
}
}
ストアは以下のように生成されるでしょう:
new Vuex.Store({
state: () => ({
counter: 0
}),
mutations: {
increment(state) {
state.counter++
}
},
modules: {
todos: {
namespaced: true,
state: () => ({
list: []
}),
mutations: {
add(state, { text }) {
state.list.push({
text,
done: false
})
},
remove(state, { todo }) {
state.list.splice(state.list.indexOf(todo), 1)
},
toggle(state, { todo }) {
todo.done = !todo.done
}
}
}
}
})
また、pages/todos.vue
では todos
モジュールを以下のように利用することができます:
<template>
<ul>
<li v-for="todo in todos" :key="todo.text">
<input :checked="todo.done" @change="toggle(todo)" type="checkbox">
<span :class="{ done: todo.done }">{{ todo.text }}</span>
</li>
<li><input @keyup.enter="addTodo" placeholder="What needs to be done?"></li>
</ul>
</template>
<script>
import { mapMutations } from 'vuex'
export default {
computed: {
todos () {
return this.$store.state.todos.list
}
},
methods: {
addTodo (e) {
this.$store.commit('todos/add', e.target.value)
e.target.value = ''
},
...mapMutations({
toggle: 'todos/toggle'
})
}
}
</script>
<style>
.done {
text-decoration: line-through;
}
</style>
モジュールメソッドはストアディレクトリにサブディレクトリを追加することなく、トップレベルでの定義に利用することができます。
ステートの例: 以下のように store/state.js
を作成します。
export default () => ({
counter: 0
})
対応するミューテーションは store/mutations.js
に置くことができます
export default {
increment(state) {
state.counter++
}
}
例のフォルダ構造
複雑なストア設定のファイル/フォルダ構造は次のようになります:
store/
--| index.js
--| ui.js
--| shop/
----| cart/
------| actions.js
------| getters.js
------| mutations.js
------| state.js
----| products/
------| mutations.js
------| state.js
------| itemsGroup1/
--------| state.js
ストアでのプラグイン
store/index.js
ファイルの中に置くことで、ストアにプラグインを追加することができます:
import myPlugin from 'myPlugin'
export const plugins = [myPlugin]
export const state = () => ({
counter: 0
})
export const mutations = {
increment(state) {
state.counter++
}
}
プラグインについてより詳細な情報はこちら: Vuex のドキュメント
nuxtServerInit アクション
ストアに nuxtServerInit
アクションが定義されていて、かつ universal
モードの場合、Nuxt はコンテキストを渡してこれを呼び出します(サーバサイドのみ)。これはサーバにある何らかのデータを直接クライアントサイドに渡すときに便利です。
例えば、サーバサイドにセッションがあり、req.session.user
で接続したユーザにアクセスできるとしましょう。認証されたユーザをストアに渡すには、store/index.js
を以下のように変更します:
actions: {
nuxtServerInit ({ commit }, { req }) {
if (req.session.user) {
commit('user', req.session.user)
}
}
}
store/index.js
内の)プライマリモジュールだけです。ここから他のモジュールのアクションに呼び出しを繋いでいく必要があります。コンテキスト は asyncData
メソッドのように、nuxtServerInit
の第 2 引数として渡されます。
nuxt generate
が走った場合、nuxtServerInit
は全ての動的に生成されたルートで実行されます。
nuxtServerInit
アクションは、Promise を返すか async/await を利用して nuxt サーバが待機できるようにしなければなりません。actions: {
async nuxtServerInit({ dispatch }) {
await dispatch('core/load')
}
}
Vuex 厳格モード
厳格モードは dev モードではデフォルトで有効になっていて、production モードでは無効になっています。 厳格モードを dev モードでも無効化するには、store/index.js
を下記の例に従ってください:
export const strict = false


















































