以下のページがVuexの勉強でとても参考になりましたので、勉強させていただきました。m(_ _)m
※この投稿にかいてあることは全て↑のリンクにかいてあることと同じです。自分の備忘録として投稿させていただきました。
webpackで環境作成
参考サイトでは@vue/cli
で環境作成を行っていましたが、私はwebpackで作成しました。
yarn init -y
yarn add vue vuex
yarn add -D webpack webpack-cli webpack-dev-server vue-loader vue-template-compiler vue-style-loader css-loader babel-loader @babel/core @babel/preset-env
const VueLoaderPlugin = require("vue-loader/lib/plugin");
module.exports = {
mode: "development",
module: {
rules: [
{
test: /\.vue$/,
loader: "vue-loader"
},
{
test: /\.js$/,
loader: "babel-loader"
},
{
test: /\.css$/,
use: ["vue-style-loader", "css-loader"]
}
]
},
plugins: [new VueLoaderPlugin()],
resolve: {
extensions: [".vue", ".js"],
alias: {
vue$: "vue/dist/vue.esm.js"
}
},
devServer: {
port: 9000,
contentBase: "./",
publicPath: "/dist/",
open: "Google Chrome"
}
};
{
"presets": [
[
"@babel/preset-env",
{
"targets": {
"node": "current"
}
}
]
]
}
import Vue from "vue"
import App from "./App"
import store from './store'
new Vue({
store,
el: "#app",
template: "<App/>",
components: { App }
})
<template>
<div id="app">
<h1>Sample Counter</h1>
<Counter />
</div>
</template>
<script>
import Counter from "./components/Counter";
export default {
components: {
Counter
}
};
</script>
<template>
<div>
<p>Count: {{ count }} !</p>
</div>
</template>
<script>
export default {
data() {
return {
count: 0
};
}
};
</script>
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
// put variables and collections here
},
mutations: {
// put sychronous functions for changing state e.g. add, edit, delete
},
actions: {
// put asynchronous functions that can call one or more mutation functions
}
})
<!DOCTYPE html>
<meta charset=utf-8>
<title>sample</title>
<script src=dist/main.js defer></script>
<div id=app></div>
yarn webpack-dev-server
この実行環境をもとにガイドを進めていきます。
Store
アプリケーション全体で参照するデータのかたまりの部分です。
store.js
で以下のように設定してみます。
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
products: [],
count: 5,
loggedInUser: {
name: 'John',
role: 'Admin'
}
},
})
$store
を使ってストアを参照する
storeに全てのデータを集約させるので、<script>
タグのdata
の部分は削除します
<template>
<div>
<p>Count: {{ $store.state.count }} !</p>
</div>
</template>
<script>
export default {};
</script>
computed
を使うことでスッキリ書くことができます。
<template>
<div>
<p>Count: {{ count }} !</p>
</div>
</template>
<script>
export default {
computed: {
count() {
return this.$store.state.count;
}
}
};
</script>
mapState
Helper
mapState
を使うことでcomputed
をさらに簡潔に書けます
<template>
<div>
<p>Welcome, {{ loggedInUser.name }}.</p>
<p>Count: {{ count }} !</p>
</div>
</template>
<script>
import { mapState } from "vuex";
export default {
computed: mapState({
count: state => state.count,
loggedInUser: state => state.loggedInUser
})
};
</script>
また、↑のようにただstoreの値とするだけなら、文字列だけをmapState
に渡すことでさらに簡潔に書けます。
↓で同じ意味となります。
<template>
<div>
<p>Welcome, {{ loggedInUser.name }}.</p>
<p>Count: {{ count }} !</p>
</div>
</template>
<script>
import { mapState } from "vuex";
export default {
computed: mapState([
"count", "loggedInUser"
])
};
</script>
computed
にstore
以外の値を渡したい場合は、スプレッド構文を使えばうまくいきます。
<template>
<div>
<p>Welcome, {{ loggedInUser.name }}.</p>
<p>Count: {{ count }} ! Count is {{ parity }}.</p>
</div>
</template>
<script>
import { mapState } from "vuex";
export default {
computed: {
...mapState([
"count", "loggedInUser"
]),
parity () {
return this.count % 2 === 0 ? "even" : "odd";
}
}
};
</script>
Getters
vuexのsotre
でgetter
を設定すると、computed
と同じように扱うことができます。
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
products: [
{ id: 1, name: "Hoge", stock: 0 },
{ id: 2, name: "Fuga", stock: 3 },
{ id: 3, name: "Piyo", stock: 0 },
],
count: 5,
loggedInUser: {
name: 'John',
role: 'Admin'
}
},
getters: {
depletedProducts: state => {
return state.products.filter(product => product.stock <= 0)
}
},
})
<template>
<div>
<ul>
<li v-for="(product, i) in depletedProducts" :key="i">
name: {{ product.name }}
</li>
</ul>
</div>
</template>
<script>
export default {
computed: {
depletedProducts() {
return this.$store.getters.depletedProducts;
}
}
};
</script>
mapGetters
Helper
こちらもmapGetters
というヘルパーが用意されており、簡潔に書くことができます。
<template>
<div>
<ul>
<li v-for="(product, i) in depletedProducts" :key="i">
name: {{ product.name }}
</li>
</ul>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
computed: {
...mapGetters([
'depletedProducts',
'anotherGetter'
])
}
};
</script>
関数を返すことで、getter
に引数を渡すこともできます。
getProductById: state => id => {
return state.products.find(product => product.id === id);
}
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
products: [
{ id: 1, name: "Hoge", stock: 0 },
{ id: 2, name: "Fuga", stock: 3 },
{ id: 3, name: "Piyo", stock: 0 },
],
count: 5,
loggedInUser: {
name: 'John',
role: 'Admin'
}
},
getters: {
depletedProducts: state => {
return state.products.filter(product => product.stock <= 0)
},
getProductById: state => id => {
return state.products.find(product => product.id === id);
}
},
})
<template>
<div>
product1: {{ getProductById(2).name }}
<ul>
<li v-for="(product, i) in depletedProducts" :key="i">
name: {{ product.name }}
</li>
</ul>
</div>
</template>
<script>
import { mapGetters } from "vuex";
export default {
computed: {
...mapGetters([
"depletedProducts",
"getProductById",
])
}
};
</script>
Mutations
store
の内容を直接変更してはいけません。
必ずmutations
から変更するようにしましょう。
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
count: 1
},
mutations: {
increment(state) {
state.count++
}
}
})
<template>
<div>
<p>Count: {{ $store.state.count }} !</p>
<button @click="updateCount">increment</button>
</div>
</template>
<script>
export default {
methods: {
updateCount() {
this.$store.commit("increment");
}
}
};
</script>
パラメータを渡すこともできます
incrementBy(state, n) {
state.count += n;
}
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
count: 0
},
mutations: {
incrementBy(state, n) {
state.count += n;
}
}
})
<template>
<div>
<p>Count: {{ $store.state.count }} !</p>
<button @click="updateCount">increment</button>
</div>
</template>
<script>
export default {
methods: {
updateCount() {
this.$store.commit('incrementBy', 25);
}
}
};
</script>
オブジェクトをパラメータに渡すこともできる。
mutations: {
incrementBy(state, { amount }) {
state.count += amount;
}
}
methods: {
updateCount() {
this.$store.commit('incrementBy', { amount: 25 });
}
}
このようなオブジェクトを渡しても大丈夫です。
methods: {
updateCount() {
this.$store.commit({
type: "incrementBy",
amount: 25
});
}
}
mapMutations
Helper
mapMutations
を使って簡潔に書くことができます。
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
count: 0
},
mutations: {
increment(state) {
state.count++;
},
incrementBy(state, { amount }) {
state.count += amount;
},
}
})
<template>
<div>
<p>Count: {{ $store.state.count }} !</p>
<button @click="increment">increment</button>
<button @click="incrementBy({ amount: 2 })">increment + 2</button>
</div>
</template>
<script>
import { mapMutations } from "vuex";
export default {
methods: {
...mapMutations([
"increment",
"incrementBy"
])
}
};
</script>
Actions
mutation
をcommitするときに、間に入って実行する機能です。
非同期処理を行うときはActionsからcommitします。
こちらが参考になりました
参考:https://vuex.vuejs.org/guide/actions.html#composing-actions
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
count: 0
},
mutations: {
increment(state) {
state.count++;
},
},
actions: {
increment: context => {
return new Promise(resolve => setTimeout(() => {
context.commit("increment");
resolve(context.state.count)
}, 1000))
}
}
})
<template>
<div>
<p>Count: {{ $store.state.count }} !</p>
<button @click="increment">increment</button>
</div>
</template>
<script>
export default {
methods: {
async increment() {
console.log(await this.$store.dispatch("increment"));
}
}
};
</script>
increment: context => {
return new Promise(resolve => setTimeout(() => {
context.commit("increment");
resolve(context.state.count)
}, 1000))
}
context
は以下それぞれ受け取ることができます。
-
context.commit
-
mutation
をcommit
する
-
-
context.state
-
state
を取得
-
-
context.getters
-
getters
を取得する
-
なのでこのように書くと簡潔になります
increment: ({ commit, state, getters}) => {
// ...
},
mapActions
Helper
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
count: 0
},
mutations: {
increment(state) {
state.count++;
},
},
actions: {
increment: ({ commit }) => commit("increment"),
incrementAsync: ({ commit, state }) => {
return new Promise(resolve => setTimeout(() => {
commit("increment");
resolve(state.count)
}, 1000))
}
}
})
<template>
<div>
<p>Count: {{ $store.state.count }} !</p>
<button @click="increment">increment</button>
<button @click="incrementAsync">incrementAsync</button>
</div>
</template>
<script>
import { mapActions } from "vuex"
export default {
methods: {
...mapActions([
"increment",
"incrementAsync",
])
}
};
</script>
Vuexを使ったカウンター作成
ここまでのを組み合わせたカウンターを作成します。
import Vue from "vue"
import Vuex from "vuex"
Vue.use(Vuex)
export default new Vuex.Store({
state: {
count: 0
},
getters: {
parity: state => state.count % 2 === 0 ? "even" : "odd"
},
mutations: {
increment(state) {
state.count++;
},
decrement(state) {
state.count--;
}
},
actions: {
increment: ({ commit }) => commit("increment"),
decrement: ({ commit }) => commit("decrement"),
incrementIfOdd: ({ commit, getters }) => getters.parity === "odd" ? commit("increment") : false,
incrementAsync: ({ commit }) => {
setTimeout(() => { commit("increment") }, 1000);
}
}
});
<template>
<div>
<p>Clicked {{ count }} times! Count is {{ parity }}.</p>
<button @click="increment">Increment</button>
<button @click="decrement">Decrement</button>
<button @click="incrementIfOdd">Increment if Odd</button>
<button @click="incrementAsync">Increment Async</button>
</div>
</template>
<script>
import { mapState, mapGetters, mapActions } from "vuex";
export default {
name: "Counter",
computed: {
...mapState(["count"]),
...mapGetters(["parity"])
},
methods: mapActions([
"increment",
"decrement",
"incrementIfOdd",
"incrementAsync"
])
};
</script>
最後まで読んでいただいてありがとうございました。m(_ _)m