vue3入门12 - 组件化 todoMvc 项目-主体内容

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第19天,点击查看活动详情

前言

这一节,我们对主体内容做一下组件封装

主体内容

  • 主体内容分为两部分,主体内容容器、todos列表
  • 我们先来封装todos列表

todos列表

  • 这个封装的就比较麻烦了,逻辑比较复杂。

  • 首先我们通过toRefs来保持props解构之后的响应式

  • 然后通过自定义v-model实现completedtext响应数据通信

  • 接着,将所有的方法通过emit发送给父组件,并且传递最新的todo数据

  • 最后,还需要把自定义指令迁移过来。

  • TodoList.vue

<template>
<li :class="{ completed: completed, editing: editingTodo == todo }">
  <div class="view">
    <input v-model="completed" class="toggle" type="checkbox" />
    <label @dblclick="editTodo()">{{ text }}</label>
    <button @click="removeTodo" class="destroy"></button>
  </div>
  <input
     v-model.trim="text"
     v-edit-focus="editingTodo == todo"
     @blur="doneEdit()"
     @keyup.enter="doneEdit()"
     @keyup.escape="cancelEdit()"
     class="edit"
     type="text"
    />
  </li>
</template>
<script>
import { defineComponent, computed, toRefs } from 'vue';
export default defineComponent({
  props: {
    todo: Object,
    editingTodo: [null, Object]
  },
  emits: ['update:completed', 'update:text', 'editTodo', 'doneEdit', 'cancelEdit', 'removeTodo'],
  setup(props, { emit }) {
    const { todo } = toRefs(props);
    const completed = computed({
      get() {
        return todo.value.completed;
      },
      set(value) {
        emit('update:completed', value);
      }
    });
    const text = computed({
      get() {
        return todo.value.text;
      },
      set(value) {
        emit('update:text', value);
      }
    });
    const editTodo = () => {
      emit('editTodo', todo.value);
    };
    const doneEdit = () => {
      emit('doneEdit', todo.value);
    };
    const cancelEdit = () => {
      emit('cancelEdit', todo.value);
    };
    const removeTodo = () => {
      emit('removeTodo', todo.value);
    };
    return {
      completed,
      text,
      editTodo,
      doneEdit,
      cancelEdit,
      removeTodo
    };
  },
  directives: {
    editFocus: (el, { value }) => value && el.focus()
  }
});
</script>
复制代码
  • 使用
<ul class="todo-list">
  <TodoList
    v-for="todo in filteredTodos"
    :key="todo.id"
    :todo="todo"
    :editingTodo="editingTodo"
    v-model:completed="todo.completed"
    v-model:text="todo.text"
    @editTodo="editTodo"
    @doneEdit="doneEdit"
    @cancelEdit="cancelEdit"
    @removeTodo="removeTodo"
  />
</ul>
复制代码
  • 起初我没有把自定义指令迁移到组件中,报了下面的错误,一直没找到原因。后来仔细看,withDirectives说的就是和自定义指令相关的错误,如果没有看过源码的同学,遇到这种错误还是要细细的排查下了。

image.png

主体容器

  • 封装好了todo列表,我们对整个主体内容做进一步封装。

  • 我们把整个todo列表操作逻辑都拆分到这个主容器中,通过更新todos列表,完成父子组件数据交互

  • Main.vue

<template>
  <section class="main">
    <CheckAll v-model:allDone="allDone" />
    <ul class="todo-list">
      <TodoList
        v-for="todo in filteredTodos"
        :key="todo.id"
        :todo="todo"
        :editingTodo="editingTodo"
        v-model:completed="todo.completed"
        v-model:text="todo.text"
        @editTodo="editTodo"
        @doneEdit="doneEdit"
        @cancelEdit="cancelEdit"
        @removeTodo="removeTodo"
      />
    </ul>
  </section>
</template>
<script>
import { defineComponent, ref, toRefs, computed } from 'vue';
import { filters } from '@/utils/index';
import CheckAll from '@/components/CheckAll.vue';
import TodoList from '@/components/TodoList.vue';
export default defineComponent({
  components: {
    CheckAll,
    TodoList
  },
  props: {
    todos: Array,
    visibility: String,
    remaining: Number
  },
  emits: ['update:todos'],
  setup(props, { emit }) {
    const { todos, remaining, visibility } = toRefs(props);
    const filteredTodos = computed(() => filters[visibility.value](todos.value));
    const todosValue = computed({
      get: () => todos.value,
      set: (v) => {
        emit('update:todos', v);
      }
    });
    const allDone = computed({
      get: () => !remaining,
      set: (value) => {
        todosValue.value.forEach((todo) => {
          todo.completed = value;
        });
      }
    });

    const editingTodo = ref(null);
    const beforeEditText = ref('');
    const editTodo = (todo) => {
      editingTodo.value = todo;
      beforeEditText.value = todo.text;
    };

    const doneEdit = (todo) => {
      if (!editingTodo.value) return;
      todo.text || removeTodo(todo);
      editingTodo.value = null;
    };

    const cancelEdit = (todo) => {
      editingTodo.value = null;
      todo.text = beforeEditText.value;
    };
    const removeTodo = (todo) => {
      todos.value.splice(todos.value.indexOf(todo), 1);
    };

    return {
      allDone,
      editingTodo,
      filteredTodos,
      editTodo,
      doneEdit,
      cancelEdit,
      removeTodo
    };
  }
});
</script>
复制代码
  • 使用
<Main v-model:todos="todos" :visibility="visibility" :remaining="remaining" />
复制代码

总结

这一节,我们主要做了主体内容的todos列表和主体容器的封装。

项目代码在GitHub,可以查阅

猜你喜欢

转载自juejin.im/post/7108149964911411207