Vue项目实战——实现一个任务清单(学以致用,两小时带你巩固和强化Vue知识点)

Vue2.x 项目实战(一)


文章目录

  • Vue2.x 项目实战(一)
    • Vue2.x 实现 todoList
      • 1、前言
      • 2、项目演示(一睹为快)
      • 3、涉及知识点
      • 4、项目详情(附源码及解析)
      • 5、写在最后的话


Vue2.x 实现 todoList

1、前言

如果你对 vue 的基础知识还很陌生,推荐先去学习一下 vue 基础

内容 参考链接
Vue2.x全家桶 Vue2.x全家桶参考链接
  • 如果你 刚学完 vue 基础知识,想检查一下自己的学习成果
  • 如果你 已学完 vue 基础知识,想快速回顾复习
  • 如果你 已精通 vue 基础知识,想做个小案例
  • 那不妨看完这篇文章,我保证你一定会有收获的!

2、项目演示(一睹为快)

todoList 项目演示

Vue项目实战——实现一个任务清单(学以致用,两小时带你巩固和强化Vue知识点)_第1张图片

3、涉及知识点

  • Vue基础:插值语法,常用指令,键盘事件,列表渲染,计算属性,事件监听,生命周期
  • Vue进阶:props(父传子),自定义事件(任意组件间通信),自定义事件的解绑,$nextTick 异步
  • 本地存储:任务记录保留在当前浏览器中,长期有效(不手动销毁则一直保留)
  • 第三方库:nonoid(下载导入即可使用)

备注:

  1. 任意组件间的通信方式有很多种(全局事件总线,消息订阅预发布…),熟练掌握一种即可(推荐自定义事件,配置简单,容易理解)
  2. 本文是 vue 基础的练习项目,不涉及 vue 周边(Vuex,Vue-router)

4、项目详情(附源码及解析)

该项目有 五个组件 构成:

(1)App.vue 父组件,以上四个子组件 最终归并的地方,并实现很多功能相关方法

(2)MyHeader.vue 子组件:头部,用于用户文本框 输入添加任务事项

(3)MyList.vue 子组件:躯干,用于 呈现任务的列表

(4)MyItem.vue 子中子组件,Mylist.vue 的子组件,用于 呈现每个任务及编辑删除

(5)MyFooter 子组件,用于 显示所选个数和总个数及删除已完成任务

App.vue 父组件

  • 所有子组件的汇集点
  • 里面定义里很多方法,通过 props 父传子,供子组件们去使用
  • 当然也有自定义事件,供子给父传值,进行页面的渲染更新
<template>
  <!-- 最外层容器 -->
  <div class="todo-container">
    <div class="todo-wrap">
      <!-- 头部子组件,子传父,自定义 addTodo事件,添加一个 todo对象 -->
      <MyHeader @addTodo="addTodo" />
      <!-- 任务列表子组件,父传子,动态绑定对应事件 -->
      <MyList :updateTodo="updateTodo" :todos="todos" :checkTodo="checkTodo" :deleteTodo="deleteTodo" />
      <!-- 底部子组件,子传父,全选和全清除 -->
      <MyFooter
        :todos="todos"
        @checkAllTodo="checkAllTodo"
        @clearAllTodo="clearAllTodo"
      />
    </div>
  </div>
</template>

<script>
// 引入所需组件
import MyHeader from "./components/MyHeader.vue";
import MyList from "./components/MyList.vue";
import MyFooter from "./components/MyFooter.vue";

export default {
  name: "App",
  components: { MyHeader, MyList, MyFooter },
  data() {
    return {
      // 由于 todos 是 MyHeader 组件 和 MyFooter 组件都在用,所以放在APP中(状态提升)
      // 解析 JSON字符串 第一次使用时 null 身上没有 length 属性会报错,所以添加||,前面不能用时,置为空数组
      // localStorage.getItem("xxx") 用于从本地存储中读取 todos
      todos: JSON.parse(localStorage.getItem("todos")) || [],
    };
  },
  methods: {
    // 添加一个 todo
    addTodo(todoObj) {
      this.todos.unshift(todoObj);
    },
    // 勾选 or 取消勾选一个todo
    checkTodo(id) {
      this.todos.forEach((todo) => {
        if (todo.id === id) todo.done = !todo.done;
      });
    },
    // 更新一个 todo
    updateTodo(id, title) {
      this.todos.forEach((todo) => {
        if (todo.id === id) todo.title = title;
      });
      
    },
    // 删除,todo.id !== id 就不会 push 该 todo,即删除
    deleteTodo(id) {
      this.todos = this.todos.filter((todo) => todo.id !== id);
    },
    // 全选 or 取消全选
    checkAllTodo(done) {
      this.todos.forEach((todo) => {
        todo.done = done;
      });
    },
    // 清除所有已经完成的todo
    clearAllTodo() {
      this.todos = this.todos.filter((todo) => {
        return !todo.done;
      });
    },
  },
  watch: {
    todos: {
      // 深度监视 检测到是否被勾选
      deep: true,
      handler(value) {
        // localStorage.setItem("xxx") 用来添加 todo
        // 格式化为 JSON 字符串
        localStorage.setItem("todos", JSON.stringify(value));
      },
    },
  },
  // 销毁前进行自定义事件的解绑
  beforeDestroy() {
    this.$off(['addTodo', 'checkAllTodo', 'clearAllTodo'])
  }
};
</script>

<style>
body {
  background: #fff;
}

.btn {
  display: inline-block;
  padding: 4px 12px;
  margin-bottom: 0;
  font-size: 14px;
  line-height: 20px;
  text-align: center;
  vertical-align: middle;
  cursor: pointer;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2),
    0 1px 2px rgba(0, 0, 0, 0.05);
  border-radius: 4px;
}

.btn-danger {
  color: #fff;
  background-color: #da4f49;
  border: 1px solid #bd362f;
}

.btn-edit {
  margin-right: 5px;
  background-color: skyblue;
  border: 1px solid rgb(102, 158, 180);
}

.btn-danger:hover {
  color: #fff;
  background-color: #bd362f;
}

.btn:focus {
  outline: none;
}

.todo-container {
  width: 600px;
  margin: 10px auto;
}

.todo-container .todo-wrap {
  padding: 10px;
  border: 1px solid #ddd;
  border-radius: 5px;
}
</style>

MyHeader.vue 组件

  • 终端键入 npm i nanoid,安装 nanoid