目的
Vue + Vuexは使ったことあるが、NuxtとTypeScriptとCompostion APIは使ったことがなかったので、勉強を兼ねて簡単なTodoアプリを作ってみる。
環境
version | |
---|---|
Vue.js | 2.6.11 |
Nuxt.js | |
TypeScript | |
Vuex |
Nuxt.js + Vuex
Todo.vue
<template>
<ul>
<li v-for="(todo, index) in todos" :key="todo">
{{ todo }}
<button @click="removeAction(index)">remove</button>
</li>
<li>
<input v-model="todoInput" />
<button @click="add">add</button>
</li>
</ul>
</template>
<script>
import { createNamespacedHelpers } from "vuex";
const { mapState, mapActions } = createNamespacedHelpers("todo");
export default {
data() {
return { todoInput: "" };
},
computed: {
...mapState({ todos: state => state.todos })
},
methods: {
...mapActions(["addAction", "removeAction"]),
add(todo) {
this.addAction(this.todoInput);
this.todoInput = "";
}
}
};
</script>
todo.js
export const state = () => ({
todos: ["task1", "task2"]
});
export const mutations = {
add(state, todo) {
state.todos.push(todo);
},
remove(state, index) {
state.todos.splice(index, 1);
}
};
export const actions = {
addAction({ commit }, todo) {
commit("add", todo);
},
removeAction({ commit }, index) {
commit("remove", index);
}
};
所感
- storeのmoduleをパッケージ切るだけで実現できるので簡単
Vue.js + Vuex
今回の簡単なTodoアプリの場合、Nuxt.jsとほぼ同じなので割愛。
Vue.js + TypeScript + Composition API
Todo.vue
<template>
<div class="hello">
<ul>
<li :key="todo" v-for="(todo, index) in todos">
{{ todo }}
<button @click="remove(index)">削除</button>
</li>
</ul>
<input v-model="todoInput" />
<button @click="addTodo">追加</button>
</div>
</template>
<script lang="ts">
import Vue from "vue";
import { createComponent, ref } from "@vue/composition-api";
import { useTodo } from "@/functions/useTodo";
export default createComponent({
setup() {
const todoInput = ref<string>("");
const { todos, add, remove } = useTodo();
function addTodo() {
// refの最新の値にアクセスするにはvalueを利用する
add(todoInput.value);
todoInput.value = "";
}
return {
todos,
addTodo,
remove,
todoInput
};
}
});
</script>
functions/useTodo.ts
import { createComponent, reactive, computed } from "@vue/composition-api";
export function useTodo() {
const state = reactive<{ todos: string[] }>({
todos: ["task1", "task2"]
});
const todos = computed(() => state.todos);
function add(todo: string) {
state.todos.push(todo);
}
function remove(index: number) {
state.todos.splice(index, 1);
}
// reactiveデータは隠ぺいしてcomputedのみ公開する
return {
todos,
add,
remove
};
}
所感
- useをprefixにつけるのは慣習らしい
- refとreactiveの使い分けがいまいち理解できていない。single objectのまま使う場合はreactiveで、separate variablesにしたい場合はrefらしい
https://vue-composition-api-rfc.netlify.com/#ref-vs-reactive - data, computed, methodsなどのoptionを共通化する(templateまで共通化するとcomponentになってしまう)
- TypeScriptだとVSCodeでコード補完や型チェックなどが効くので、コーディングがすごく楽
- 同一ファイル内で業務的なまとまりごとに分割するだけでもコードの見通しは良くなりそう
Vue.js + Vuex + TypeScript + Class-based
Class-basedはVue3から使われなくなるため、Composition APIを利用したほうがよい。
あくまで参考情報として記載する。
Todo.vue
<template>
<div class="hello">
<ul>
<li :key="todo" v-for="(todo, index) in todos">
{{ todo }}
<button @click="remove(index)">削除</button>
</li>
</ul>
<input v-model="todoInput" />
<button @click="add">追加</button>
</div>
</template>
<script lang="ts">
import { Component, Prop, Vue } from "vue-property-decorator";
import { todoModule } from "@/store/todo";
@Component
export default class HelloWorld extends Vue {
todoInput: string = "";
get todos() {
return todoModule.todos;
}
add() {
todoModule.addAction(this.todoInput);
this.todoInput = "";
}
remove(index: number) {
todoModule.removeAction(index);
}
}
</script>
store/todo.ts
import {
Mutation,
Action,
VuexModule,
Module,
getModule
} from "vuex-module-decorators";
import store from "@/store";
@Module({ dynamic: true, store, name: "todoModule" })
class TodoModule extends VuexModule {
todos: string[] = ["task1", "task2"];
@Mutation
add(todo: string) {
this.todos.push(todo);
}
@Mutation
remove(index: number) {
this.todos.splice(index, 1);
}
@Action
addAction(todo: string) {
this.context.commit("add", todo);
}
@Action
removeAction(index: number) {
this.context.commit("remove", index);
}
}
export const todoModule = getModule(TodoModule);