参考にさせて頂いたページ
はじめに
APIにリクエストする非同期な処理を想定して、actionsからcommitしています。
実際にAPIとか作ったらもう少し違う実装が適切かもしれないです。
ES2015が使えない環境下でのことを考慮するなど個人的事情からlodashを使用していたり、とはいえ全体的にはES2015で書いていますがそこはそっとスルーでお願いします。
今回のバージョン
- Vue.js v2.1.10
- vuex v2.3.0
例として以下の通りです。
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta charset="utf-8">
<title></title>
<meta name="description" content="">
<meta name="author" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="css/style.css">
<link rel="shortcut icon" href="">
</head>
<body>
<style>
.is-done {
color: #999999;
text-decoration: line-through;
}
</style>
<h1>タスク管理アプリケーション</h1>
<div id="app">
</div>
<script src="js/lodash.js"></script>
<script src="js/vue.js"></script>
<script src="js/vuex.js"></script>
<script>
/**
* store
*/
const store = new Vuex.Store({
strict: true,
state: {
latestItemId: null,
items: []
},
getters: {
},
actions: {
addItem({commit, rootState}, content) {
// 実際はここでAPIにリクエストしてDBのレコード追加が成功したらcommit
const newItemId = rootState.latestItemId + 1;
commit('addItem', {id: newItemId, content: content, isDone: false});
},
toggleItem({commit, rootState}, id) {
// 実際はここでAPIにリクエストしてDBの当該レコードの更新が成功したらcommit
const index = _.findIndex(rootState.items, function(o) { return o.id == id; });
commit('toggleItem', index);
},
deleteItem({commit}, id) {
// 実際はここでAPIにリクエストしてDBの当該レコードの削除が成功したらcommit
commit('deleteItem', id);
},
getItems({commit}) {
// 実際はここでAPIにリクエストしてDBからタスクリストを取得します
const items = [
{id: 0, content: '起きる', isDone: false},
{id: 1, content: '朝食を食う', isDone: false},
{id: 2, content: '昼食を食う', isDone: true},
{id: 4, content: '夕食を食う', isDone: true},
{id: 5, content: '風呂に入る', isDone: false},
{id: 10, content: '寝る', isDone: false},
];
const latestItemId = items[items.length - 1].id;
commit('getItems', {latestItemId: latestItemId, items: items});
}
},
mutations: {
addItem(state, newItem) {
state.latestItemId = newItem.id;
state.items.push(newItem);
},
toggleItem(state, index) {
state.items[index].isDone = !state.items[index].isDone;
},
deleteItem(state, id) {
state.items = _.filter(state.items, function(item) {
return item.id !== id;
});
},
getItems(state, newState) {
_.assign(state, newState);
}
}
});
/**
* addItem
* タスク新規追加フィールドのコンポーネント
*/
const addItem = {
data() {
return {
content: '',
}
},
template: `
<section>
<h2>タスクを新しく追加</h2>
<div>
<input type="text" v-model="content">
<input type="submit" value="追加" @click="addItem">
</div>
</section>
`,
methods: {
addItem() {
if (!this.content) return;
this.$store.dispatch('addItem', this.content);
this.content = '';
}
}
};
/**
* item
* タスク一つ分のliタグのコンポーネント
*/
const item = {
props: ['item'],
template: `
<li>
<span :class="{'is-done': isDone}">id {{item.id}} : {{item.content}}</span>
<button @click="toggleItem" v-text="buttonLabel"></button>
<button @click="deleteItem">削除する</button>
</li>
`,
computed: {
id() {
return this.item.id;
},
isDone() {
return this.item.isDone;
},
buttonLabel() {
return this.item.isDone ? '未完了に戻す' : '完了にする';
},
},
methods: {
toggleItem() {
this.$store.dispatch('toggleItem', this.id);
},
deleteItem() {
if (!confirm("削除しますか?")) return;
this.$store.dispatch('deleteItem', this.id);
},
}
};
/**
* itemList
* タスク一覧のulタグのコンポーネント
*/
const itemList = {
components: {item},
template: `
<section>
<h2>登録されているタスク</h2>
<ul>
<item v-for="item in items" v-bind:item="item"></item>
</ul>
</section>
`,
computed: {
items() {
return this.$store.state.items;
}
},
created() {
this.$store.dispatch('getItems');
}
};
/**
* app
* rootのコンポーネント
*/
const app = new Vue({
el: '#app',
store,
components: {
addItem,
itemList,
},
template: `
<div class="app">
<add-item />
<item-list />
</div>
`
});
</script>
</body>
</html>
Vuexとは
Storeを提供し、以下のようなルールに則った開発をすることでコードの見通しがよくなります。
- Storeが持っているstateはmutationsによってのみ更新される
- mutaionsは同期的でなくてはならず、非同期な処理はactionsが担う
正確な情報は公式ドキュメントを参照してください。
個人的まとめ
あくまでも個人的なもので、ES2015などを使用しない環境下のせいもあると思いますが、参考になればと思います。
- VuexはStoreを提供するもので、コンポーネントは普通にVue.js単体で使っているときと同じ要領で書く
- rootコンポーネントにstoreオプションを設定すると、子コンポーネントで
this.$store
が参照できるようになる
- コンポーネント内でstoreを参照するときは
this.$store
を使う
- Storeは
strict: true,
を設定した方がいい
- Storeが持っているstateはmutationsによってのみ更新される
- mutaionsは同期的でなくてはならず、非同期な処理はactionsが担う
- mutationsが持っているメソッドは第一引数に
state
を受け取る
- actionsが持っているメソッドは第一引数に
context
または分割で{state, rootState, commit, dispatch, getters}
を受け取る
- mutaionを呼ぶときはcommitで、actionを呼ぶときはdispatch
- getters, modules, plugins というキーもあるけど今回は使っておらず、いずれも大きい規模になるほど有用そう
- actionのdispatchとpromiseについては「アクション · GitBook」
Vuexに限らずVue.jsのコンポーネントのこと
- コンポーネント内において、Storeが持っているstateの更新が反映されてほしい箇所はcomputedでreturnする
- コンポーネント内において、親からもらったpropsの更新が反映されてほしい箇所は直接templateに書くか、computedでreturnする
- アロー関数を使うとthisがコンポーネント自身を参照しないので理解して使う