Vue.js(Composition API)+TypeScriptの環境でVuex・Vue Router・axiosを使ってみる#3:Vuexで状態管理
最終更新日: Update!!
今回の記事でも、Vue.jsのComposition APIでTypeScriptを使ったSPAを作成していきたいと思います。第3回目はVuexを使ったアプリケーションの状態管理を見ていきます。これまでのPropsやEmitのバケツリレーのようにコンポーネント間で値を受け渡しを行う場合、コンポーネントの構造が複雑になったり、ネストが深くなるとどうしても見通しが悪くなってしまいますし。ログイン状態の有無など、複数のコンポーネントを跨ぐ場合には扱いにくくなってしまいます。そこで、変数となるデータを集約して管理する方法がとられます。
Vue.jsの場合には「Vuex」というライブラリがスタンダードで、今回もそれを使った前提の内容になっています。ただ、本記事執筆段階では「Pinia」というVuexに変わる新しいライブラリも登場しているようです。公式でも推奨されているようで、こちらについてはまた別記事でまとめてみたいと思います。
これまでの開発環境やコンポーネントについては過去記事をご参考ください。本記事はこの記事の内容をベースに進めています。
・Vue.js(Composition API)+TypeScriptの環境でVuex・Vue Router・axiosを使ってみる#1:環境構築
・Vue.js(Composition API)+TypeScriptの環境でVuex・Vue Router・axiosを使ってみる#2:コンポーネント作成・PropsとEmit
それでは早速コードを見ていきます。今回の状態管理を追加するにあたって、「store.ts」という状態管理専用のスクリプトと、Vuexの型定義ファイルを新たに追加しています。(下記は一部抜粋している内容です)
........ ┣ src ┗ ts ┣ main.ts ┣ store.ts <- 追加 ┗ vue ┣ app.vue ┗ components ┣ ListDraft.vue ┣ ItemDraft.vue ┣ ListOfficial.vue ┣ ItemOfficial.vue ┗ UpdateButton.vue ┣ ...... ┗ types ┣ vue.d.ts ┗ vuex.d.ts <- 追加続いてVuexのモジュールをインストールしていきます。Vue.jsの3系を使う場合にはバージョンに注意して最新版をインストールしていきます。
$ npm install --save vuex@nextモジュールの方が追加されましたので、設定を行っていきます。
{ ..... "dependencies": { ..... "vuex": "^4.0.2" ..... }, }まずは型定義ファイルを用意していきます。この中で状態管理で扱うストアデータの型定義を指定しておきます。 【types/vuex.d.ts】
import { ComponentCustomProperties } from 'vue'; import { Store } from 'vuex'; declare module '@vue/runtime-core' { interface State { ..... // storeの型定義 ..... } interface ComponentCustomProperties { $store: Store<State> } }続いて、エントリーポイント側のスクリプトでVuexのモジュールを読み込んでいきます。合わせて後述するストアデータ用のファイルも読み込んでアプリケーション内で使えるようにしておきます。 【main.ts】
import { createApp } from 'vue'; import { store, key } from './store'; import App from './vue/app.vue'; const app = createApp({}); app.component('app', App) .use(store, key) .mount('#app');最後に状態管理用のストアファイルを見ていきます。基本的にはVue.jsの2系と同様、「state」、「mutations」、「actions」、「getters」とそれぞれ定義(過去記事「Vue CLIとVuexでアプリケーションの状態変化を扱う」でも紹介しています)していく形になり、TypeScriptの場合には型づけが追加されるようになります。ただし、Vue.js3系では、ストア内のデータや処理について「this.$store」で参照せずに「useStore」でアクセスする形になりますので、useStoreを使えるようにエクスポートしておく点に注意します。 【store.ts】
import { InjectionKey } from 'vue'; import { createStore, Store, useStore as baseUseStore } from 'vuex'; interface itemInterface { id: number, isChecked: boolean, title: string } export interface State { draftItems: itemInterface[], officialItems: itemInterface[] } export const key: InjectionKey<Store<State>> = Symbol(); export const store = createStore<State>({ state: { draftItems: [], officialItems: [] }, mutations: { setDraftItems(state, payload: itemInterface[]) { state.draftItems = payload; }, setOfficialItems(state, payload: itemInterface[]) { state.officialItems = payload; } }, actions: { updateDraftItems({ commit }, payload: itemInterface[]) { commit('setDraftItems', payload); }, updateOfficialItems({ commit }, payload: itemInterface[]) { commit('setOfficialItems', payload); } }, getters: { getDraftItems(state) { return state.draftItems; }, getOfficialItems(state) { return state.officialItems; } } }); export const useStore = () => { return baseUseStore(key); }最後にコンポーネント側でステートの値を取得したり更新処理を行なっていきます。値の取得はストアファイルの「getters」にアクセスすることで可能となります。また値の更新は「actions」の処理を「dispatch()」メソッドを使って呼び出すか、あるいは、直接コンポーネントファイル側から「commit()」メソッドで「mutations」を呼び出す形で、stateの値を更新していきます。 【app.vue】
<template> <section> <list-draft :props-items="store.getters.getDraftItems" /> <list-official :props-items="store.getters.getOfficialItems" /> </section> <update-button @update-items="update()" /> </template> <script lang="ts" setup> import { reactive, computed } from 'vue'; import { useStore } from 'vuex'; import { key } from '../store'; import ListDraft from './components/ListDraft.vue'; import ListOfficial from './components/ListOfficial.vue'; import UpdateButton from './components/UpdateButton.vue'; interface dataInterface { id: number, isChecked: boolean, title: string, } const store = useStore(key); store.dispatch('updateDraftItems', [ { id: 1, isChecked: false, title: 'チェック項目1' }, { id: 2, isChecked: true, title: 'チェック項目2' }, { id: 3, isChecked: false, title: 'チェック項目3' }, ]); const data: { officialItems: dataInterface[] } = reactive({ officialItems: [] }); const checkedItems = computed(() => { return store.getters.getDraftItems.filter((item: dataInterface) => { return item.isChecked; }); }); const update = () => { data.officialItems = checkedItems.value; store.dispatch('updateOfficialItems', data.officialItems); } </script>状態管理を使わない場合ですと、基本的にコンポーネント側のデータ変数で値を保持しておくイメージですが、ここではストアファイルの値を呼び出したり、更新したりするため、コンポーネント側で保持するデータがすっきりしますね。 今回のサンプルではあまり影響を受けませんが、親コンポーネントからのPropsではなく子コンポーネントから直接データを取得したり、子コンポーネントから値の更新ができるようになることで、処理もシンプルになりますね。使い分けながらうまく状態管理を活用していくことも重要ですね。 (こちらの記事も合わせてどうぞ) Vue.js(Composition API)+TypeScriptの環境でVuex・Vue Router・axiosを使ってみる#1:環境構築 Vue.js(Composition API)+TypeScriptの環境でVuex・Vue Router・axiosを使ってみる#2:コンポーネント作成・PropsとEmit
sponserd
keyword search
recent posts
- Shopifyでオリジナルテーマ制作やテーマカスタマイズで使えるTips #2
Shopifyでオリジナルテーマ制作やテーマカスタマイズで使えるTips #2
- Shopifyでオリジナルテーマ制作やテーマカスタマイズで使えるTips #1
Shopifyでオリジナルテーマ制作やテーマカスタマイズで使えるTips #1
- ViteでReactとReact Routerを使ってみる
ViteでReactとReact Routerを使ってみる
- ViteでPugのコンパイル環境を導入する
ViteでPugのコンパイル環境を導入する
- ViteでMarkuplintとPrettierを使える環境を構築する
ViteでMarkuplintとPrettierを使える環境を構築する
- ViteでStylelintとESlintを使える環境を構築する
ViteでStylelintとESlintを使える環境を構築する
- マウスオーバーしたセルを含む行列がハイライトするテーブルを作成する:has()擬似クラスの活用例
マウスオーバーしたセルを含む行列がハイライトするテーブルを作成する:has()擬似クラスの活用例
- ViteでVue.jsとVuex(Pinia)とVue Routerを使ってみる
ViteでVue.jsとVuex(Pinia)とVue Routerを使ってみる
categories