あっくんブログ Written by Akihiro Tsuji

Vue3のVuexによる状態管理

Vue.js プログラミング

こんにちは、あっくんです。日々の習慣に睡眠があります。早起きをして、色々やっていきたいのですが、最近全然、起きれない状態です。といっても早く寝ないので、早く起きれない循環ですね。職業訓練が終わって、就活がうまく行かずダレてきましたが、行動の予定を立てて明日から自分にプレッシャーをかけて再び早起きに挑戦してみます。今回はVue3のVuexによる状態管理です。

コンポーネント間の値の管理

Vue.jsで開発をするとどんどんコンポーネントが増えていくことになります。増えていくコンポーネントを補完する場所を用意されるプラグインがあります。それが「Vuex」です。Vuexはさまざまなコンポーネントで共通して扱える「ストア」と呼ばれる保管場所を提供します。

Vuexを用意する

Vuexの利用はCDNとnpmでパッケージをインストールする方法があります。

CDNによる利用

CDN(Content DeliverryNetwork)を利用して、Vuexのスクリプトを読み込む方法です。ベースのindex.htmlを開いてheadタグ内に↓を追記します。

<script src="https://unpkg.com/vuex@next/dist/vuex.cjs.js"></script>

npmによるインストール

まずvuex_appをViteで作成してでサンプルを作っていきましょう。

開発するディレクトリでターミナルに↓のように記述してvuex_appを作って、npmをインストールします。

npm init vite-app vuex_app

cd vuex_app
npm install

Vuexをインストールしていきましょう。

npm install vuex

Image from Gyazo

↑npm installでvuexをインストールします。

Vuexの基本を理解する

Vuexは「ストア」を使って値を管理します。

スクリプトを用意する
Vuexでは、ストア管理するためのスクリプトを用意する必要があります。

$storeを利用する
コンポーネントなどからVuexの変数や処理などを利用するためには、$storeという値が用意されます。

ファイルの作成

Vuexでスクリプトのファイルを用意します。vuexフォルダに「store.js」という名前で用意します。

store.jsにスクリプトを記述する

store.js

import { createStore } from "vuex"; export const store = createStore({ state() { return { message: "This is store data.", }; }, });
Code language: JavaScript (javascript)

スクリプトの内容をチェック

import {createStore} from 'vuex'

‘vuex’がパッケージ名です。「createStore」という関数をインポートします。

export const store = createStore(….)

引数にはストアに関する情報をまとめたオブジェクトを指定します。そして、代入した定数をそのままexportします。

ステートに値を保管する

createStoreの引数のオブジェクトに「store」というメソッドを用意します。

state() {  return オブジェクトジェクト }
Code language: JavaScript (javascript)

コンポーネントのdataに相当するものをイメージしてもいいですね。

storeをアプリケーションに組み込む

index.htmlとmain.jsを修正していきましょう。

index.html

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <link rel="icon" href="/favicon.ico" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Vuex App</title> <!-- CSS only --> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-giJF6kkoqNQ00vy+HMDP7azOuL0xtbfIcaT9wjKHr8RbDVddVHyTfAAsrekwKmP1" crossorigin="anonymous" /> <!-- JavaScript Bundle with Popper --> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/js/bootstrap.bundle.min.js" integrity="sha384-ygbV9kiqUc6oa4msXn9868pTtWMgiQaeYH7/t7LECLbyPA2x65Kgf80OJFdroafW" crossorigin="anonymous" ></script> </head> <body> <h1 class="bg-secondary text-white h4 p-3">Vue3 Vuex</h1> <div class="container"> <div id="app"></div> </div> <script type="module" src="/src/main.js"></script> </body> </html>
Code language: HTML, XML (xml)

main.js

import { createApp } from "vue"; import App from "./App.vue"; import "./index.css"; import { store } from "./store.js"; const app = createApp(App); app.use(store); app.mount("#app");
Code language: JavaScript (javascript)

importでstore.jsをインポートし、それをapp.useでアプリケーションに組み込んでいます。

Appを修正する

App.vue

<template> <div id="app"> <HelloWorld /> </div> </template> <script> import HelloWorld from "./components/HelloWorld.vue"; export default { name: "App", components: { HelloWorld, }, }; </script>
Code language: HTML, XML (xml)

ストアの値を利用する

ストアの値をtemplateで利用します。HelloWorldを書き換えます。

HelloWorld.vue

<template> <div class="alert alert-primary"> <h1>{{ data.title }}</h1> <p class="mt-3 h5">{{ $store.state.message }}</p> </div> </template> <script> import { ref, reactive } from "vue"; export default { setup(props) { const data = reactive({ title: "Vuex", }); return { data, }; }, }; </script>
Code language: HTML, XML (xml)
Image from Gyazo

↑ターミナルでnpm run devを打ち込んでアクセスすると、ストアのmessageを表示します。

$storeがストアに保管された値がまとめられているオブジェクトです。そのなかのstateのmessageを取り出して表示しています。

ミューテーションを使う

ミューテーションとは?

ミューテーションとはストアにあるステートの値を直接操作する処理を用意します。
storeオブジェクト作成時に用意するオブジェクト内に↓のように記述します。

mutations: { 名前: (変数)=> {…..処理, ………必要なだけ用意 },
Code language: JavaScript (javascript)

mutationsは、関数をまとめたオブジェクトとして値を用意します。この関数には、stateオブジェクトが渡されます。

ミューテーションをコミットする

ミューテーションの機能をコンポーネント側から呼び出すには「commit」というメソッドを使います。

{{ $store.commit( 名前 ) }}

引数には実行するミューテーションの名前を指定します。指定されたミューテーションの実行がストア側に送られ、ストアは適当なタイミングでそこにある処理を呼び出し、ステートの更新などが行われます。

counterを操作するミューテーション

store.js

import { createStore } from "vuex"; export const store = createStore({ state() { return { message: "This is store data.", counter: 0, }; }, mutations: { count: (state) => { state.counter++; }, reset: (state) => { state.counter = 0; }, }, });
Code language: JavaScript (javascript)

countはstate.counterはcounterの値を1増やし、resetはstate.counterをゼロに戻します。

クリックしてカウントする

HelloWorld.vueを修正します。

HelloWorld.vue

<template> <div class="alert alert-primary"> <h1>{{ data.title }}</h1> <p class="mt-3 h5">{{ $store.state.message }}</p> <hr /> <div class="btn btn-secondary" @click="$store.commit('count')" @click.ctrl="$store.commit('reset')" > <a class="h5 text-white text-decoration-none" >clicked:{{ $store.state.counter }}</a > </div> </div> </template> <script> import { ref, reactive } from "vue"; export default { setup(props) { const data = reactive({ title: "Vuex", }); return { data, }; }, }; </script>
Code language: HTML, XML (xml)
Image from Gyazo

↑クリックするとカウントします。ctrlキーを押してクリックするとゼロに戻ります。

@ = v-on

@clickはv-on:clickのことで、v-onは@で代用することができます。

ミューテーションの引数指定

ミューテーションは、commitで名前を指定するだけです。引数には自stateが動的に渡されます。そして、追加で引数を渡す場合は,stateの後に追加します。

store.js

import { createStore } from "vuex"; export const store = createStore({ state() { return { message: "This is store data.", counter: 0, }; }, mutations: { count: (state, n) => { state.counter += n; }, reset: (state) => { state.counter = 0; }, }, });
Code language: JavaScript (javascript)

HelloWorld.vue

<template> <div class="alert alert-primary"> <h1>{{ data.title }}</h1> <p class="mt-3 h5">{{ $store.state.message }}</p> <hr /> <div class="btn-lg btn-dark" @click.exact="$store.commit('count', 1)" @click.shift="$store.commit('count', 2)" @click.ctrl="$store.commit('count', 3)" > <a class="h5 text-white text-decoration-none" @click.stop="$store.commit('reset')" > clicked:{{ $store.state.counter }}</a > </div> </div> </template> <script> import { ref, reactive } from "vue"; export default { setup(props) { const data = reactive({ title: "Vuex", }); return { data, }; }, }; </script>
Code language: HTML, XML (xml)
Image from Gyazo

↑普通にクリックすると1,Shiftキーを押しながらだと2,Ctrlキーを押しながらだと3ずつ増えるようになります。ShiftキーとCtrlキーを同時に押してクリックすれば、5ずつ増えていきます。@click.exact="$store.commit('count',1)"のcommitの第2引数がcountミューテーションの第2引数nに渡されています。

typeを利用したオブジェクト引数

typeプロパティを使って、必要な値を1つのオブジェクトにまとめて引数に渡すことはできます。store.jsファイルとHelloWorld.vueを修正しましょう。

store.js

import { createStore } from "vuex"; export const store = createStore({ state() { return { message: "This is store data.", counter: 0, }; }, mutations: { count: (state, obj) => { state.message = obj.message; state.counter += obj.add; }, reset: (state) => { state.message = "reset!"; state.counter = 0; }, }, });
Code language: JavaScript (javascript)

HelloWorld.vue

<template> <div class="alert alert-primary"> <h1>{{ data.title }}</h1> <p class="mt-3 h5">{{ $store.state.message }}</p> <hr /> <div class="btn-lg btn-dark" @click.exact="$store.commit({ type: 'count', message: 'add 1!', add: 1 })" @click.shift.exact=" $store.commit({ type: 'count', message: 'add 5!', add: 5 }) " @click.ctrl.exact=" $store.commit({ type: 'count', message: 'add 10!', add: 10 }) " > <a class="h5 text-white text-decoration-none" @click.stop="$store.commit('reset')" > clicked:{{ $store.state.counter }}</a > </div> </div> </template> <script> import { ref, reactive } from "vue"; export default { setup(props) { const data = reactive({ title: "Vuex", }); return { data, }; }, }; </script>
Code language: HTML, XML (xml)
Image from Gyazo

↑クリックすると、数字とメッセージが更新されます。普通にクリックすると1,Shift+クリックで5,Ctrl+クリックで10増えていきます。

$store.commit({ type: 'count', message: ‘add 1!’, add: 1 })

typeプロパティを使ってすべての引数を1つのオブジェクトにまとめてミューテーションを呼び出すことができます。

アクションを利用する

ミューテーションは、ステートを利用した処理を実行するためのものですが、その他に「アクション」というものがあります。アクションは複数のミューテーションを続けて呼び出す場合に用いられます。

action: { 名前: (引数) =>{ …….呼び出す処理出す処理 }, ………必要なだけ記述 }
Code language: JavaScript (javascript)

引数にはcontextというオブジェクトが渡されます。

カウンターとメッセージで更新する

store.jsとHelloWorld.vueを書き換えましょう。

store.js

import { createStore } from "vuex"; export const store = createStore({ state: () => { return { message: "counter number.", counter: 0, }; }, mutations: { count: (state, n) => { state.counter += n; }, say: (state, msg) => { state.message = msg; }, reset: (state) => { state.counter = 0; state.message = "reset!!"; }, }, actions: { doit: (context) => { const n = Math.floor(Math.random() * 10); context.commit("count", n); context.commit("say", "add" + n + "!"); }, }, });
Code language: JavaScript (javascript)

HelloWorld.vue

<template> <div class="alert alert-primary"> <h1>{{ data.title }}</h1> <p class="mt-3 h5">{{ $store.state.message }}</p> <hr /> <div class="btn-lg btn-dark" @click="$store.dispatch('doit')"> <a class="h5 text-white text-decoration-none" @click.stop="$store.commit('reset')" > clicked:{{ $store.state.counter }}</a > </div> </div> </template> <script> import { ref, reactive } from "vue"; export default { setup(props) { const data = reactive({ title: "Vuex", }); return { data, }; }, }; </script>
Code language: HTML, XML (xml)
Image from Gyazo

↑クリックすると0~9までの整数がランダムに追加され、メッセージが更新されます。

@click="$store.dispatch('doit')" 

$store.dispatchでアクションが実行され、そこからcount,sayミューテーションが呼び出されていきます。

vuex-persistedstateを利用する

ページをリロードするとcounterの初期値に戻ります。ページをリロードしても値を保持するには、vuex-persistedstateというプラグインプログラムを利用します。vuex-persistedstateはVuexのステートをローカルストレージに保管し、終了後も消えないようにするプラグインです。vuex-persistedstateはCDNとnpmをインストールする方法が用意されています。

CDNを利用する

ベースのHTMLファイルのbody>タグに↓を追記します。

<script src="https://unpkg.com/vuex-persistedstate/dist/vuex-persistedstate.umd.js"></script>

npmでインストールする

vuex-persistedstateはnpmで配布されていますので、npm installでインストールした使えます。↓のコマンドを実行しましょう。

npm install vuex-persistedstate

Image from Gyazo

Vuexでvuex-persistedstateを利用する

store.jsファイルにvuex-persistedstateを組み込んでいきましょう。

store.js

import { createStore } from "vuex"; import createPersistedState from "vuex-persistedstate"; export const store = createStore({ state: () => { return { message: "count number.", counter: 0, }; }, mutations: { doit: (state) => { const n = Math.floor(Math.random() * 10); state.counter += n; state.message = "add" + n + "."; }, reset: (state) => { state.counter = 0; state.message = "reset now."; }, }, plugins: [createPersistedState()], });
Code language: JavaScript (javascript)

これで、ステート関係がすべてローカルストレージに保存され、ブラウザを閉じたりした後もずっと値が保持されるようになりました。

ステートの動作を確かめる

HelloWorld.vueを修正します。

HelloWorld.vue

<template> <div class="alert alert-primary"> <h1>{{ data.title }}</h1> <p class="mt-3 h5">{{ $store.state.message }}</p> <hr /> <div class="btn-lg btn-dark" @click="$store.commit('doit')"> <a class="h5 text-white text-decoration-none" @click.stop="$store.commit('reset')" > clicked:{{ $store.state.counter }}</a > </div> </div> </template> <script> import { ref, reactive } from "vue"; export default { setup(props) { const data = reactive({ title: "Vuex", }); return { data, }; }, }; </script>
Code language: HTML, XML (xml)
Image from Gyazo

↑クリックすると0~9の値をランダムに追記していきます。ある程度操作した後にリロードすると、初期値戻ることのなく保存されています。アプリケーションを終了して、「npm run dev」で再起動しても最後の値が保存されています。

ここまで読んでいただいてありがとうございます。
Vuexというプラグインを使うことでストアという保管場所が用意されています。この保管場所に作成したコンポーネントを保管して管理してより良い開発ができるといいですね。
掌田津耶乃さんのVue.js3超入門で勉強をしています。語りかけるような文章ですごいわかりやすいです。本当におすすめです。