nuxt-ts day3:型安全なVuexでtodoリストを作る
3日目。

https://nuxt-ts-sample.netlify.app/todolist
作った。
compotisionAPI で todoリストを作る
todolist.vue
import { reactive } from "@vue/composition-api";
interface Todo {
  todo: string,
  todos: string[]
}
export default {
  setup() {
    const state = reactive<Todo>({
      todo: '',
      todos: []
    })
    const addTodo = () => {
      state.todos.push(state.todo)
      state.todo = ''
    }
    const removeTodo = (index: number) => state.todos.splice(index,1)
    return {
      state,
      addTodo,
      removeTodo
    }
  }
}
シンプルな形です。
Vuex を導入する
公式のドキュメントに従って進めていきます。そもそもvuexが型推論と相性が悪いようなので、色々と過程を踏む必要があるらしい。
モジュールとして、store の内容を記述します。
store\todo.ts
import { Module, VuexModule, Mutation } from 'vuex-module-decorators'
@Module({
  name: 'todo',
  stateFactory: true,
  namespaced: true
})
export default class Todos extends VuexModule {
  private todos: string[] = ['task1']
  public get getTodos () {
    return this.todos
  }
  @Mutation
  public add (todo: string) {
    this.todos.push(todo)
  }
  @Mutation
  public remove (id: number) {
    this.todos.splice(id, 1)
  }
}
ここでモジュールを store として読み込みます。
store\index.ts
import { Store } from 'vuex'
import { initialiseStores } from '~/utils/store-accessor'
const initializer = (store: Store<any>) => initialiseStores(store)
export const plugins = [initializer]
export * from '~/utils/store-accessor'
アクセサーを作ります。これでcomponentから TodoStore が参照できるようになりました。
新たに store を作りたいときはここに追記していく必要があります。
utils\store-accessor.ts
/* eslint-disable import/no-mutable-exports */
import { Store } from 'vuex'
import { getModule } from 'vuex-module-decorators'
import Todo from '~/store/todo'
let TodoStore: Todo
function initialiseStores (store: Store<any>): void {
  TodoStore = getModule(Todo, store)
}
export { initialiseStores, TodoStore }
todolistコンポーネントを store 仕様に書き換えます。
todolist.vue
import { defineComponent, reactive, computed } from '@vue/composition-api'
import { TodoStore } from '~/store'
interface Todo {
  todo: string
}
export default defineComponent({
  setup () {
    const state = reactive<Todo>({
      todo: ''
    })
    const todos = TodoStore
    const todolist = computed(() => todos.getTodos)
    const addTodo = () => {
      todos.add(state.todo)
      state.todo = ''
    }
    const removeTodo = (index: number) => {
      todos.remove(index)
    }
    return {
      state,
      todolist,
      addTodo,
      removeTodo
    }
  }
})
tailwindcss の使用感
template部はこんな感じに。tailwindcssをやっている。
todolist.vue
<template>
  <div class="justify-center items-center text-center max-w-md mx-auto">
    <h1>Todoリスト</h1>
    <div class="flex">
      <input v-model="state.todo" class="bg-white focus:outline-none focus:shadow-outline border border-gray-300 rounded-lg py-2 px-4 block w-full leading-normal" placeholder="taskを入力してください">
      <button class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded w-20 ml-3" @click="addTodo">
        追加
      </button>
    </div>
    <br>
    <li v-for="(todo, index) in todolist" :key="index" class="py-1 text-left">
      {{ todo }}
      <button class="bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 ml-3 rounded-full" @click="removeTodo(index)">
        delete
      </button>
    </li>
</template>
現段階ではいまいち便利さを図り損ねている。微調整がかんたんなのは嬉しいけど、新たなコンポーネントを作るたびに設定しなきゃなのが面倒ですね。CSSの勉強にはなりそうだ。