お問い合わせ

ブログ

これまでに経験してきたプロジェクトで気になる技術の情報を紹介していきます。

vue3 vuex

okuda Okuda 3 years

vuex

Vuexとは、Vue.jsアプリケーション用の状態管理ライブラリ
アプリケーションが保持するデータで、どのコンポーネントからでもVuex内に保持するデータへのアクセスが可能になリ、一元管理ができるもの

vuexのインストール

CDN

    <script src="https://unpkg.com/vue@next"></script>
    <script src="https://unpkg.com/vuex@next"></script>

NPM

npm i vuex@next

package.json

    //...
    "dependencies": {
        "vue-router": "^4.0.10",
        "vuex": "^4.0.2" // 追加されている
    }
    //...

vueの準備

<html>

    <head>
        <script src="https://unpkg.com/vue@next"></script>
        <script src="https://unpkg.com/vuex@next"></script>
    </head>

    <body>
        <div id="app">
            {{message}}
        </div>
    </body>

</html>

<script>
    // appを作成
    const app = {
        data() {
            return {
                message: 'hello'
            }
        }
    }

    // vueにマウント
    Vue.createApp(app)
        .mount('#app')
</script>

vuex store作成

ストアには以下の4つコンセプトのがある

  • ステート
  • ゲッター
  • ミューテーション
  • アクション
//...
// storeを追加
const store = new Vuex.createStore({
    /*
     * ステート(データの入れ物)
     * state:コンポーネントでいうdata
     */
    state() {},
    /*
     * ゲッター(ステートから算出される値)
     * getters:コンポーネントでいうcomputed的なもの
     */
    getters() {},
    /*
     * ミューテーション(同期処理)
     * mutations:コンポーネントでいうmethod(setter)
     * stateを変更できるのはミューテーションのみ
     * 同期処理でなければならない
     * commitで呼び出す
     */
    mutations() {},
    /*
     * アクション(非同期処理)
     * actionのコミットを使うことでミューテーションを呼び出す(コンポーネントには無い概念)
     * 状態を変更するのではなく、ミューテーションをコミット
     * 任意の非同期処理を含むことができる
     * dispatchで呼び出す
     */
    actions() {},
})

// appを作成
const app = {
    //...
}

Vue.createApp(app)
    .use(store) // storeを追加
    .mount('#app')
//...

ステート

  • ステート(データの入れ物)
  • state:コンポーネントでいうdata

ステートの設定と呼び出し

    // storeを追加
    const store = new Vuex.createStore({
        // stateにcountを作成
        state: {
            count: 0
        },
    });

    // appを作成
    const app = {
        data() {
            return {
                message: 'vuex!'
            }
        },
        // ステートを呼び出し
        computed: {
            // 標準的な呼び出し
            count1() {
                return this.$store.state.count;
            },
        }
    }
    //...
    <div id="app">
        <h1>{{message}}</h1>
        <h2>state</h2>
        <!-- カウントを表示 -->
        <div>{{count1}}</div>
    </div>

mapState ヘルパ

mapState ヘルパを使用する場合はいくつかの方法がある

// mapStateをインポート
import {
    mapState
} from "vuex";

// storeを追加
const store = new Vuex.createStore({
    // stateにcountを作成
    state: {
        count: 0
    },
});

// appを作成
const app = {
    data() {
        return {
            message: 'test!'
        }
    },
    // ステートを呼び出し
    computed: {
        // 標準的な呼び出し
        count1() {
            return this.$store.state.count;
        },
        // ヘルパをオブジェクトスプレッド演算子で記述
        ...Vuex.mapState({
            // アロー関数
            count2: (state) => state.count,
            // 文字列では呼び出し
            count3: "count",
        }),
        // 配列の文字列で記述
        ...Vuex.mapState([
            "count"
        ]),
    }
}
//...
    <div id="app">
        <h1>{{message}}</h1>
        <h2>state</h2>
        <div>count1: {{count1}}</div>
        <div>count2: {{count2}}</div>
        <div>count3: {{count3}}</div>
        <div>count: {{count}}</div>
    </div>

ゲッター

  • ゲッター(ステートから算出される値)
  • getters:コンポーネントでいうcomputed的なもの

ゲッターの設定と呼び出し

const store = new Vuex.createStore({
    state: {
        count: 0
        // stateにtodos配列を作成
        todos: [{
                id: 1,
                text: '1 text true',
                done: true
            },
            {
                id: 2,
                text: '2 text true',
                done: true
            },
            {
                id: 3,
                text: '3 text false',
                done: false
            }
        ]
    },
    // ゲッターを追加
    getters: {
        // done=trueのものを取得
        doneTodos(state) {
            return state.todos.filter(todo => todo.done)
        },
        // done=trueをカウント
        doneTodosCount(state, getters) {
            return getters.doneTodos.length
        },
        // idで取得するファンクションを返す
        getTodoById: (state) => (id) => {
            return state.todos.find(todo => todo.id === id)
        }
    }
});

const app = {
    //...
    computed: {
        //...
        // 標準的な取得
        doneTodos1() {
            console.log(this.$store.getters)
            return this.$store.getters.doneTodos
        },
        doneTodosCount1() {
            return this.$store.getters.doneTodosCount
        },
        getTodoById1() {
            return this.$store.getters.getTodoById
        },
        // ヘルパをオブジェクトスプレッド演算子で記述
        ...Vuex.mapGetters({
            doneTodos2: "doneTodos",
            doneTodosCount2: "doneTodosCount",
            getTodoById2: "getTodoById",
        }),
        // 配列の文字列で記述
        ...Vuex.mapGetters([
            "doneTodos",
            "doneTodosCount",
            "getTodoById",
        ]),
    }
}
//...
    <div id="app">
        <h2>getters</h2>
        <!-- 標準的な取得 -->
        <div>doneTodos1: {{doneTodos1}}</div>
        <div>doneTodosCount1: {{doneTodosCount1}}</div>
        <div>getTodoById1(3): {{getTodoById1(3)}}</div>
        <!-- ヘルパをオブジェクトスプレッド演算子で記述 -->
        <div>doneTodos2: {{doneTodos2}}</div>
        <div>doneTodosCount2: {{doneTodosCount2}}</div>
        <div>getTodoById2(3): {{getTodoById2(3)}}</div>
        <!-- 配列の文字列で記述 -->
        <div>doneTodos: {{doneTodos}}</div>
        <div>doneTodosCount: {{doneTodosCount}}</div>
        <div>getTodoById(3): {{getTodoById(3)}}</div>
    </div>

ミューテーション

  • mutations:コンポーネントでいうmethod(setter)
  • stateを変更できるのはミューテーションのみ
  • 同期処理でなければならない
  • commitで呼び出す

ミューテーションの設定と呼び出し

const store = new Vuex.createStore({
    //...
    // ミューテーションを追加
    mutations: {
        // countを増加させる
        increment(state, n = 1) {
            state.count += n
        }
    }
});

const app = {
    //...
    methods: {
        // mutations
        // 標準的な取得
        increment1(n) {
            this.$store.commit("increment", n);
        },
        // ヘルパをオブジェクトスプレッド演算子で記述
        ...Vuex.mapMutations({
            increment2: "increment",
        }),
        // 配列の文字列で記述
        ...Vuex.mapMutations(["increment"]),
    },
}
//...
    <div id="app">
        <h2>mutations</h2>
        <button @click="increment1(1)">increment1(1)</button>
        <button @click="increment2(10)">increment2(10)</button>
        <button @click="increment(100)">increment(100)</button>
    </div>

アクション

  • アクション(非同期処理)
  • actionのコミットを使うことでミューテーションを呼び出す(コンポーネントには無い念)
  • 状態を変更するのではなく、ミューテーションをコミット
  • 任意の非同期処理を含むことができる
  • dispatchで呼び出す

アクションの設定と呼び出し

const store = new Vuex.createStore({
    //...
    //アクションを追加
    actions: {
        delayIncrement(context, n) {
            return new Promise((resolve, reject) => {
                setTimeout(() => {
                    context.commit('increment', n)
                    resolve()
                }, 2000)
            })
        }
    }
});

const app = {
    //...
    methods: {
        // 標準的な取得
        delayIncrement1(n) {
            this.$store.dispatch("delayIncrement", n);
        },
        // ヘルパをオブジェクトスプレッド演算子で記述
        ...Vuex.mapActions({
            delayIncrement2: "delayIncrement",
        }),
        // 配列の文字列で記述
        ...Vuex.mapActions(["delayIncrement"]),
    },
}
//...
    <div id="app">
        <h2>actions</h2>
        <button @click="delayIncrement1(1)">delayIncrement1(1)</button>
        <button @click="delayIncrement2(10)">delayIncrement2(10)</button>
        <button @click="delayIncrement(100)">delayIncrement(100)</button>
    </div>

モジュール

モジュールを作成して分けることができる

モジュールの作成

以下の部分を moduleA に書き換えます

//...
const store = new Vuex.createStore({
    namespaced: true,
    state: {
        //...
    },
    getters: {
        //...
    },
    mutations: {
        //...
    },
    actions: {
        //...
    }
});

このように編集

//...
const moduleA = {
    // 名前をストア別に区別できるようにネームスペースを使う
    namespaced: true,
    state: {
        //...
    },
    getters: {
        //...
    },
    mutations: {
        //...
    },
    actions: {
        //...
    }
};

モジュールをマージしてストアを作成

const moduleA = {
    //...
};

// モジュールBも追加
const moduleB = {
    // 名前をストア別に区別できるようにネームスペースを使う
    namespaced: true,
    state: {
        count: 99999,
    },
}

// モジュールをマージしてストアを作成
const store = new Vuex.createStore({
    // モジュールに登録
    modules: {
        moduleA,
        moduleB,
    }
})

呼び出しにネームスペースを追加

モジュールしてネームスペースで区別できるようにしたので、呼び出し方も変更する

    //...
    // ステートを呼び出し
    computed: {
            // state
            // 標準的な呼び出し
            count1() {
                return this.$store.state.moduleA.count
            },
            // ヘルパをオブジェクトスプレッド演算子で記述
            ...Vuex.mapState('moduleA', {
                // アロー関数
                count2: (state) => state.count,
                // 文字列では呼び出し
                count3: "count",
            }),
            // 配列の文字列で記述
            ...Vuex.mapState('moduleA', [
                "count"
            ]),

            // moduleB
            ...Vuex.mapState("moduleB", {
                countB: "count"
            }),

            // getters
            // 標準的な取得
            doneTodos1() {
                return this.$store.getters["moduleA/doneTodos"];
            },
            doneTodosCount1() {
                return this.$store.getters["moduleA/doneTodosCount"]
            },
            getTodoById1() {
                return this.$store.getters["moduleA/getTodoById"]
            },
            // ヘルパをオブジェクトスプレッド演算子で記述
            ...Vuex.mapGetters('moduleA', {
                doneTodos2: "doneTodos",
                doneTodosCount2: "doneTodosCount",
                getTodoById2: "getTodoById",
            }),
            // 配列の文字列で記述
            ...Vuex.mapGetters('moduleA', [
                "doneTodos",
                "doneTodosCount",
                "getTodoById",
            ]),
        },
        methods: {
            // mutations commit
            // 標準的な取得
            increment1(n) {
                this.$store.commit("moduleA/increment", n)
            },
            // ヘルパをオブジェクトスプレッド演算子で記述
            ...Vuex.mapMutations("moduleA", {
                increment2: "increment",
            }),
            // 配列の文字列で記述
            ...Vuex.mapMutations("moduleA", ["increment"]),

            // action dispatch
            // 標準的な取得
            delayIncrement1(n) {
                this.$store.dispatch("moduleA/delayIncrement", n);
            },
            // ヘルパをオブジェクトスプレッド演算子で記述
            ...Vuex.mapActions("moduleA", {
                delayIncrement2: "delayIncrement",
            }),
            // 配列の文字列で記述
            ...Vuex.mapActions("moduleA", ["delayIncrement"]),
        },
    //...
        <!-- 追加 -->
        <h2>moduleB</h2>
        <div>moduleB count: {{countB}}</div>

参考コード

<html>

<head>
    <script src="https://unpkg.com/vue@next"></script>
    <script src="https://unpkg.com/vuex@next"></script>
</head>

<body>
    <div id="app">
        <h1>{{message}}</h1>
        <h2>state</h2>
        <div>count1: {{count1}}</div>
        <div>count2: {{count2}}</div>
        <div>count3: {{count3}}</div>
        <div>count: {{count}}</div>

        <h2>getters</h2>
        <div>doneTodos1: {{doneTodos1}}</div>
        <div>doneTodosCount1: {{doneTodosCount1}}</div>
        <div>getTodoById1(3): {{getTodoById1(3)}}</div>

        <div>doneTodos2: {{doneTodos2}}</div>
        <div>doneTodosCount2: {{doneTodosCount2}}</div>
        <div>getTodoById2(3): {{getTodoById2(3)}}</div>

        <div>doneTodos: {{doneTodos}}</div>
        <div>doneTodosCount: {{doneTodosCount}}</div>
        <div>getTodoById(3): {{getTodoById(3)}}</div>

        <h2>mutations</h2>
        <button @click="increment1(1)">increment1(1)</button>
        <button @click="increment2(10)">increment2(10)</button>
        <button @click="increment(100)">increment(100)</button>

        <h2>actions</h2>
        <button @click="delayIncrement1(1)">delayIncrement1(1)</button>
        <button @click="delayIncrement2(10)">delayIncrement2(10)</button>
        <button @click="delayIncrement(100)">delayIncrement(100)</button>

        <h2>moduleB</h2>
        <div>moduleB count: {{countB}}</div>
    </div>
</body>

</html>

<script>
    const moduleA = {
        namespaced: true,
        state: {
            count: 0,
            // stateにtodos配列を作成
            todos: [{
                    id: 1,
                    text: '1 text true',
                    done: true
                },
                {
                    id: 2,
                    text: '2 text true',
                    done: true
                },
                {
                    id: 3,
                    text: '3 text false',
                    done: false
                }
            ],
        },
        getters: {
            // done=trueのものを取得
            doneTodos(state) {
                return state.todos.filter(todo => todo.done)
            },
            // done=trueをカウント
            doneTodosCount(state, getters) {
                return getters.doneTodos.length
            },
            // idで取得するファンクションを返す
            getTodoById: (state) => (id) => {
                return state.todos.find(todo => todo.id === id)
            }
        },
        mutations: {
            increment(state, n = 1) {
                state.count += n
            }
        },
        actions: {
            delayIncrement(context, n) {
                return new Promise((resolve, reject) => {
                    setTimeout(() => {
                        context.commit('increment', n)
                        resolve()
                    }, 2000)
                })
            }
        }
    };

    const moduleB = {
        namespaced: true,
        state: {
            count: 99999,
        },
    }

    const store = new Vuex.createStore({
        // モジュールに登録
        modules: {
            moduleA,
            moduleB,
        }
    })

    // appを作成
    const app = {
        data() {
            return {
                message: 'vuex!'
            }
        },
        // ステートを呼び出し
        computed: {
            // state
            // 標準的な呼び出し
            count1() {
                return this.$store.state.moduleA.count
            },
            // ヘルパをオブジェクトスプレッド演算子で記述
            ...Vuex.mapState('moduleA', {
                // アロー関数
                count2: (state) => state.count,
                // 文字列では呼び出し
                count3: "count",
            }),
            // 配列の文字列で記述
            ...Vuex.mapState('moduleA', [
                "count"
            ]),

            // moduleB
            ...Vuex.mapState("moduleB", {
                countB: "count"
            }),

            // getters
            // 標準的な取得
            doneTodos1() {
                return this.$store.getters["moduleA/doneTodos"];
            },
            doneTodosCount1() {
                return this.$store.getters["moduleA/doneTodosCount"]
            },
            getTodoById1() {
                return this.$store.getters["moduleA/getTodoById"]
            },
            // ヘルパをオブジェクトスプレッド演算子で記述
            ...Vuex.mapGetters('moduleA', {
                doneTodos2: "doneTodos",
                doneTodosCount2: "doneTodosCount",
                getTodoById2: "getTodoById",
            }),
            // 配列の文字列で記述
            ...Vuex.mapGetters('moduleA', [
                "doneTodos",
                "doneTodosCount",
                "getTodoById",
            ]),
        },
        methods: {
            // mutations commit
            // 標準的な取得
            increment1(n) {
                this.$store.commit("moduleA/increment", n)
            },
            // ヘルパをオブジェクトスプレッド演算子で記述
            ...Vuex.mapMutations("moduleA", {
                increment2: "increment",
            }),
            // 配列の文字列で記述
            ...Vuex.mapMutations("moduleA", ["increment"]),

            // action dispatch
            // 標準的な取得
            delayIncrement1(n) {
                this.$store.dispatch("moduleA/delayIncrement", n);
            },
            // ヘルパをオブジェクトスプレッド演算子で記述
            ...Vuex.mapActions("moduleA", {
                delayIncrement2: "delayIncrement",
            }),
            // 配列の文字列で記述
            ...Vuex.mapActions("moduleA", ["delayIncrement"]),
        },
    }

    Vue.createApp(app)
        .use(store) // storeを追加
        .mount('#app')
</script>
vue3 vuex 2021-11-19 09:09:31

コメントはありません。

4141

お気軽に
お問い合わせください。

お問い合わせ
gomibako@aska-ltd.jp