LoginSignup
4
5

More than 3 years have passed since last update.

簡単なTodoアプリをNuxt.jsとTypeScriptとComposition APIで作ってみた

Last updated at Posted at 2019-12-25

目的

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でコード補完や型チェックなどが効くので、コーディングがすごく楽
  • 同一ファイル内で業務的なまとまりごとに分割するだけでもコードの見通しは良くなりそう
    Composition API

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);
4
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
5