Vue实现 TodoList

        本篇咱们需要完成一个小案例,该案例的效果如下:

         接着我们就来一步一步实现这个案例吧~

一、组件化编码流程:

        (1).拆分静态组件:组件要按照功能点拆分,命名不要与html元素冲突。

        (2).实现动态组件:考虑好数据的存放位置,数据是一个组件在用,还是一些组件在用:

                1).一个组件在用:放在组件自身即可。

                2). 一些组件在用:放在他们共同的父组件上(状态提升)。

        (3).实现交互:从绑定事件开始。

拆分好的页面结构如下:

                将所有的结构、样式搭建好后我们就开始添加交互效果。

二、添加交互效果

1.初始化页面

        首先我们先生成假数据,完成页面初始化(勾选框)。在这里我们可以先把假数据放在App组件下。由App组件管理数据。

2.todoHeader组件

        在todoheader中输入数据,按回车添加一条数据。注意,此时生成的数据需要通知给App组件。在此处,我们是将子组件的数据传递给父组件,我们可以在父组件传递一个函数给子组件,让子组件调用,但显然这不是最优解,最优解是给父组件绑定一个自定义事件。

        相关代码如下:      

//App组件
methods: {
    todoAdd(todo) {
      // console.log("我收到了数据:", todo);
      this.todos.unshift(todo);
    },
}
// todoHeader
 methods: {
    // 添加一个
    add() {
      if (!this.title) return alert("输入不能为空");
      const todoObj = { id: nanoid(), title: this.title, done: false };
      this.$emit("todoAdd", todoObj);
      this.title = "";
    },
  },

3.todoList组件(todoItem组件)

        接着我们完成的是中间的todoList板块,List中又嵌套着todoItem组件,所以操作都是在todoItem上进行的。主要是完成添加勾选、删除功能。在初始化时,我们将数据放在App组件上,而真正需要数据的是todoItem组件,这里我们可以使用全局事件总线或者是消息的订阅与发布去实现组件间的通信。

//main.js 
 beforeCreate() {
    Vue.prototype.$bus = this // 安装全局事件总线
  }
// App组件
methods: {
//TodoItem
    todoCheck(id) {
      this.todos.forEach((todo) => {
        if (todo.id === id) todo.done = !todo.done;
      });
    },
    todoDetele(_, id) {
      this.todos = this.todos.filter((todo) => {
        return todo.id !== id;
      });
    },
}
 mounted() {
    // 全局事件总线
    this.$bus.$on("todoCheck", this.todoCheck);
    // 消息的订阅与发布
    this.pubId = pubsub.subscribe("todoDetele", this.todoDetele);
}
  beforeDestroy() {
    this.$bus.$off("todoCheck");
    pubsub.unsubscribe(this.pubId);
  },
}
//item 
methods: {
    handleCheck(id) {
      // 通知App更改数据
      this.$bus.$emit("todoCheck", id);
    },
    handleDelete(id) {
      //通知App更改数据
      if (confirm("确定删除吗?")) {
        // this.$bus.$emit('todoDetele',id)
        pubsub.publish("todoDetele", id);
      }
    },
}

         注意:在使用全局事件总线时,如果传递的参数不需要时,需要用_占位。否则Vue中严格检查会报错。        

4.todoFooter组件

        在这个版块也是由子组件传递数据给父组件,使用自定义事件即可。和todoHeader的做法是类似的。在这个板块中我们要实现的功能就是底部全选、任务列表、清除已完成任务。   

5.将TodoList变成本地存储

        其实至此我们的todoList案例已经初具雏形,但是这个待办事项应该是一个本地存储。当我们下次打开这个网页时,上次的记录应保留。

        于是我们就需要使用到webStorage来帮助我们存储。

        Window.sessionStorage--存储的内容会随着浏览器窗口关闭而消失

        Window.localStorage --存储的内容,需要手动清除才会消失。

        以上两种存储方式,它们的API是完全相同的。主要区别就在是否是会存储到本地。相关API如下:

        ☆添加:xxxxxStorage.setItem('key', 'value');
        ☆获取:xxxxxStorage.getItem('person');
        ☆删除:xxxxxStorage.removeItem('key');
        ☆清空:xxxxxStorage.clear()

        在必要的时候搭配JSON.stringify和JSON.parse方法使用。

   注意:xxxxxStorage.getItem(xxx) 如果xxx对应的value获取不到,那么getItem的返回值是null。JSON.parse(null)的结果依然是null。

        在这个案例中,我们需要把初始化生成的假数据删掉,换成我们动态添加的数据,并将这些数据存储到本地。那么如何能通知App组件数据变化了呢,我们之前学过一个属性,专门用来监视数据,即watch属性。

        相关代码如下:

// App组件
  data() {
    return {
      todos: JSON.parse(localStorage.getItem("todos")) || [],
    };
  },
  watch: {
    todos: {
      deep: true,
      handler(value) {
        localStorage.setItem("todos", JSON.stringify(value));
      },
    },
  },

        注意:我们存储的是一个对象,当要监听这个对象里面的属性和方法时,我们需要开启深度监视。 

6.添加编辑功能

        一个基础的待办事项应该具有增删改查的功能。所以为了让功能更加齐全,我们为每个todo添加上编辑功能。逻辑是:当我们点击编辑按钮,文件变成输入框,可以修改文字。当输入框失去焦点时,完成修改。在修改文字时,编辑按钮消失。

        那么我们就需要再写一个输入框,绑定同样的数据,用条件判断是否显示。书写完样式后就开始绑定各种事件。

<template>
  <li>
    <label>
      <input
        type="checkbox"
        :checked="todo.done"
        @change="handleCheck(todo.id)"
      />
      <span v-show="!todo.isEdit">{{ todo.title }}</span>
      <input
        type="input"
        v-show="todo.isEdit"
        :value="todo.title"
        @blur="handleBlur(todo, $event)"
        ref="inputTitle"
      />
    </label>
    <button class="btn btn-danger" @click="handleDelete(todo.id)">删除</button>
    <button
      class="btn btn-edit"
      @click="handleEdit(todo)"
      v-show="!todo.isEdit"
    >
      编辑
    </button>
  </li>
</template>
methods: {
 handleEdit(todo) {
      if (todo.isEdit) {
        //  if todo.hasOwnProperty("isEdit")
        todo.isEdit = true;
      } else {
        this.$set(todo, "isEdit", true);
      }
      this.$nextTick(function () {
        this.$refs.inputTitle.focus();
      });
    },
    handleBlur(todo, e) {
      todo.isEdit = false;
      if (!e.target.value.trim()) return alert("输入不能为空!");
      this.$bus.$emit("todoUpdata", todo.id, e.target.value);
    },
}

6.添加过渡/动画效果

        最后为了优化用户体验感,添加了一个过渡效果。

过渡与动画

        作用:在插入、更新或移除 DOM元素时,在合适的时候给元素添加样式类名。

使用:

        1.样式

/* 元素进入的样式 */
 1. v-enter:进入的起点
 2. v-enter-active:进入过程中
 3. v-enter-to:进入的终点
/*元素离开的样式 */
 1. v-leave:离开的起点
 2. v-leave-active:离开过程中
 3. v-leave-to:离开的终点

        2.用法

        需要使用<transition>包裹要过渡的元素,并且配置name属性。

        注意:若有多个元素要过渡,使用<transition-group>,且要指定key。

具体是动画还是过渡效果看如何使用:

动画:css中的动画样式+todo-enter-active实现动画

/* 动画 */
.todo-enter-active {
  animation: atguigu 0.5s linear;
}
.todo-leave-active {
  animation: atguigu 0.5s linear reverse;
} 
@keyframes atguigu {
  from {
    transform: translateX(100%);
  }
  to {
    transform: translateX(0px);
  }
}

过渡:定义过渡的起点和终点,以及在过渡时间和效果

/* 过渡 */
.todo-enter-active,
.todo-leave-active {
  transition: 0.5s linear;
}
.todo-enter {
  transform: translateX(100%);
}
.todo-enter-to,
.toddo-leave {
  transform: translateX(0);
}
.todo-leave-to {
  transform: translateX(-100%);
}

        当然,我们也可以使用一些第三方库实现更好的动画效果。在这里我们不过多做探讨。至此,一个简单的TodoList就完成啦。本篇只是大概梳理了一下每个组件的实现逻辑,练习组件间的通信方式。

        这是使用vue脚手架完成的第一个小项目,感觉之前学习的知识终于能用上了,而且脚手架中帮我们把很多东西都完成了初始化,让我们写代码的效率噌噌噌往上涨!

双手给你点赞 GIF 动图表情包_动图_点赞_gif表情

 

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>