<!-- Use preprocessors via the lang attribute! e.g. <template lang="pug"> -->
<template>
  <div id="app" :key="list.length" class="p-4">
    <div class="flex items-center justify-center">
      <input
        type="text"
        v-model="todo"
        class="block border rounded-md px-3 py-1"
        v-on:keyup.enter="onAdd"
        ref="todoInput"
        placeholder="Enter todo title ..."
      />
    </div>
    <ul class="mt-4">
      <li
        v-for="todo in list"
        :key="todo.id"
        class="flex items-center justify-between p-3 border rounded-md m-2"
        :class="{ 'bg-gray-100': todo.done }"
      >
        <div class="space-x-3">
          <input type="checkbox" v-model="todo.done" />
          <span :class="{ 'line-through': todo.done }" class="font-medium">
            {{ todo.title }}
          </span>
        </div>
        <button @click="onRemove(todo.id)">x</button>
      </li>
    </ul>
  </div>
</template>

<script>
import { ref, reactive, nextTick, toRefs, computed } from "vue";

export default {
  setup() {
    const todoInput = ref(null);
    const todo = ref("");

    const getStandardResource = () =>  {
    const getData = () => {
        return reactive({
          primaryKey: 'id',
          resource: new Map(),
          list: computed({
            get: () => Array.from(pageData.resource.values()),
            set: (newData) => {
              let resource = new Map()
              Array.from(newData, (value, _key) => {
                resource.set(value[pageData.primaryKey], value)
              })
              pageData.resource = resource
            }
          })
        })
      }
      const pageData = getData();
      return {
        pageData
      }
    }

    const {pageData} = getStandardResource();

    pageData.list = [
      { id: 0, title: "Do this", done: false },
      { id: 1, title: "Do that", done: true },
      { id: 2, title: "Do these", done: true }
    ]
    const onAdd = async function () {
      try {
        let list = Array.from(pageData.resource.values())
        
        list.push({
          id: new Date().getTime(),
          title: todo.value,
          code: Math.random() + 100
        });
        pageData.list = list;
        console.log(  pageData.list )
        todo.value = "";
        await nextTick();
        todoInput.value.focus();
      } catch (err) {
        console.log(err.message);
      }
    };

    const onRemove = (id) => {
      try {
        pageData.resource.delete(id);
      } catch (err) {
        console.debug(err.message);
      }
    };

    return {
      ...toRefs(pageData),
      todo,
      todoInput,
      onAdd,
      onRemove
    };
  }
};
</script>

<!-- Use preprocessors via the lang attribute! e.g. <style lang="scss"> -->
<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

External CSS

  1. https://cdnjs.cloudflare.com/ajax/libs/tailwindcss/2.2.16/tailwind.min.css

External JavaScript

This Pen doesn't use any external JavaScript resources.