一个输入框Demo带你理解vue3新特性(filters过滤器、reactive、toRefs、computed、 watchEffect、directives、localStorage)

<template>
  <div>
    <!-- 新增todo -->
    <input
      type="text"
      v-model="newTodo"
      @keyup.enter="addTodo"
      autofocus
      placeholder="新增今日待办"
      autocomplete="off"
    />
    <!-- todo列表 -->
    <ul>
      <li
        v-for="todo in filtedTodos"
        :key="todo.id"
        :class="{ completed: todo.completed, editing: todo === editedTodo }"
      >
        <!-- 绑定完成状态 -->
        <div class="view">
          <input type="checkbox" v-model="todo.completed" />
          <label @dblclick="editTodo(todo)">{{ todo.title }}</label>
          <button @click="removeTodo(todo)">X</button>
        </div>
        <!-- 编辑待办 -->
        <input
          type="text"
          class="edit"
          v-model="todo.title"
          v-todo-focus="todo === editedTodo"
          @blur="doneEdit(todo)"
          @keyup.enter="doneEdit(todo)"
          @keyup.escape="cancelEdit(todo)"
        />
      </li>
    </ul>
    <!-- 过滤 -->
    <p class="filters">
      <span
        @click="visibility = 'all'"
        :class="{ seleted: visibility === 'all' }"
        >All</span
      >
      <span
        @click="visibility = 'active'"
        :class="{ seleted: visibility === 'active' }"
        >Active</span
      >
      <span
        @click="visibility = 'completed'"
        :class="{ seleted: visibility === 'completed' }"
        >Completed</span
      >
    </p>
  </div>
</template>

<script>
import { reactive, toRefs, computed, watchEffect } from "vue";
// 过滤器
const filters = {
  all(todos) {
    return todos;
  },
  active(todos) {
    return todos.filter((todo) => !todo.completed);
  },
  completed(todos) {
    return todos.filter((todo) => todo.completed);
  },
};

// 缓存操作
const todoStorage = {
  fetch() {
    let todos = JSON.parse(localStorage.getItem("vue3-todos") || "[]");
    todos.forEach((todo, index) => {
      todo.id = index + 1;
    });
    return todos;
  },
  save(todos) {
    localStorage.setItem("vue3-todos", JSON.stringify(todos));
  },
};

export default {
  setup() {
    const state = reactive({
      newTodo: "",
      todos: todoStorage.fetch(), //获取是否有缓存数据
      beforeEiditCache: "", // 缓存编辑前的title
      editedTodo: null, //正在编辑的todo
      visibility: "all",
      filtedTodos: computed(() => {
        return filters[state.visibility](state.todos);
      }),
    });

    function addTodo() {
      state.todos.push({
        id: state.todos.length + 1,
        title: state.newTodo,
        completed: false,
      });
      state.newTodo = "";
    }

    function removeTodo(todo) {
      state.todos.splice(state.todos.indexOf(todo), 1);
    }

    function editTodo(todo) {
      state.beforeEiditCache = todo.title;
      state.editedTodo = todo;
    }

    function cancelEdit(todo) {
      todo.title = state.beforeEiditCache;
      state.editedTodo = null;
    }

    function doneEdit(todo) {
      state.editedTodo = null;
    }
    watchEffect(() => {
      // 执行一些操作,其中必须含有响应式变量(state.todos是响应式变量)
      todoStorage.save(state.todos);
    });
    return {
      ...toRefs(state),
      addTodo,
      removeTodo,
      editTodo,
      cancelEdit,
      doneEdit,
    };
  },
  //   自定义指令
  directives: {
    "todo-focus": (el, { value }) => {
      if (value) {
        el.focus();
      }
    },
  },
};
</script>

<style scoped>
.completed label {
  text-decoration: line-through;
}
.edit,
.editing .view {
  display: none;
}
.view,
.editing .edit {
  display: block;
}
.filters > span {
  padding: 2px 4px;
  margin-right: 4px;
  border: 1px solid transparent;
}
.filters > span.seleted {
  border-color: rgba(173, 47, 47, 0.2);
}
</style>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值