当前位置: 首页 > news >正文

vue3实现 TodoList 运用到的点 组件传值props, ref, onMounted, reactive, toRefs, watch

vue3 实现简单的todolist,
运用到的知识点: 组件传值props, ref, onMounted, reactive, toRefs, watch
实现功能如下图。
在这里插入图片描述
在追加一个全部删除按钮
在这里插入图片描述
在这里插入图片描述
在app里添加

// 全部删除
   const delAll = (id) => {
      state.todos = state.todos.filter((val) => {
        console.log(val.id, "val");
        val.id !== id;
      });
    };
  记得传值过去就行

介绍下,我这里进行了封装和父子组件传值。

在这里插入图片描述
App文件(父组件)

<template>
  <div class="todo-container">
    <div class="todo-wrap">
      <Herder :addTodo="addtodo" />
      <List :todos="todos" :deleteTodo="deleteTodo" :updataTodo="updataTodo" />
      <Footer :todos="todos" :checkAll="checkAll" :deleteAll="deleteAll" :delAll="delAll" />
    </div>
  </div>
</template>
<script lang="ts">
import { defineComponent, onMounted, reactive, toRefs, watch } from "vue";
import Herder from "./components/com/headTo.vue";
import List from "./components/com/listTo.vue";
import Footer from "./components/com/foterTo.vue";
// 定义接口,约束state数据类型
import Todo from "./components/type/type";
import { readTods, saveTodos } from "./components/utils/index";
export default defineComponent({
  name: "App",
  components: { Herder, List, Footer },
  setup() {
    // 初始化数据是一个数组形式,数组中的每个数据都是对象
    const state = reactive<{ todos: Todo[] }>({
      todos: [],// 初始化为空数组
    });
    // 加载完成后在读取数据
    onMounted(() => {
      setTimeout(() => {
        state.todos = readTods(); // 此处进行了本地存储的封装,
      }, 1000);
    });
    // 添加数据事件,是在输入框中执行,就在head 头部结构中进行
    const addtodo = (todo: Todo) => {
      state.todos.unshift(todo); //头部增加
      // state.todos.push(todo);  尾部增加
    };
    // 删除数据, list 表格中进行传值
    const deleteTodo = (index: number) => {
      state.todos.splice(index, 1);
    };
    // 修改todo的isComputed属性的状态 , list 表格中进行传值
    const updataTodo = (todo: Todo, isCompleted: boolean) => {
      todo.isCompleted = isCompleted;
      console.log(todo, "todo");
    };
    // 全选,全不选。 在底部的footer中执行
    const checkAll = (isCompleted: boolean) => {
      state.todos.forEach((todo) => {
        todo.isCompleted = isCompleted;
      });
    };
    // 删除所有选中,  在底部的footer中执行
    const deleteAll = () => {
      state.todos = state.todos.filter((todo) => !todo.isCompleted);
    };
     // 全部删除
   const delAll = (id) => {
      state.todos = state.todos.filter((val) => {
        console.log(val.id, "val");
        val.id !== id;
      });
    };
    // 监听操作,若todos数组变化直接存储到缓存中
    // watch(
    //   () => state.todos,
    //   (value) => {
    //     localStorage.setItem("todos——key", JSON.stringify(value));
    //   },
    //   { deep: true }
    // );
    
    watch(() => state.todos, saveTodos, { deep: true });  // 设置缓存

    return {
      ...toRefs(state),
      addtodo,
      deleteTodo,
      updataTodo,
      checkAll,
      deleteAll,
      delAll 
    };
  },
});
</script>
<style scoped>
	.todo-container {
	  width: 600px;
	  margin: 0 auto;
	}
	.todo-container .todo-wrap {
	  padding: 10px;
	  border: 1px, solid #000;
	  border-radius: 5px;
	}
</style>

type封装就一点不在上代码,看图就行
在这里插入图片描述
utils 存储封装也很少
在这里插入图片描述
head 头部代码

	<template>
	  <div class="todo-header">
	    <input
	      type="text"
	      placeholder="输入,回车结束"
	      v-model="title"
	      @keyup.enter="add"
	    />
	    <!-- add事件,是在输入框中进行的,
	    就要推送给父组件 app ,
	    我没有运用context上下文去emit 推送(context.emit),
	    直接用了 setup(props,context)中的第一个参数props 操作的 -->
	  </div>
	</template>
	<script>
	import { defineComponent, ref } from "vue";
	export default defineComponent({
	  name: "headTo",
	  props: { 
	  // 接收父组件传递的参数
	    addTodo: { 
	      type: Function,
	      required: true,
	    },
	  },
	  setup(props) {
	    const title = ref("");
	    const add = () => {
	      // 当获取的值为非空在添加
	      const text = title.value;
	      if (!text.trim()) return; // 没有数据
	      // 有数据,就添加信息值
	      const todo = {
	        id: Date.now(),
	        title: text,
	        isCompleted: false, // 新数据是默认不选中的
	      };
	      // 调用addTodo方法
	      props.addTodo(todo);
	      // 清空文本
	      title.value = "";
	    };
	    return {
	      title,
	      add,
	    };
	  },
	});
	</script>
	<style scoped>
	.todo-header input {
	  width: 560px;
	  height: 28px;
	  font-size: 14px;
	  border: 1px salmon #ccc;
	  border-radius: 4px;
	  padding: 4px 7px;
	}
	.todo-header input:focus {
	  outline: none;
	  border-color: rgba(82, 168, 236, 0.8);
	  box-shadow: rgba(82, 168, 236, 0.6);
	}
	</style>
	此时头部操作结束。

list文件

	<template>
	  <ul class="todo-main">
	    <Item
	      v-for="(todo, index) in todos"
	      :key="todo.id"
	      :todo="todo"
	      :deleteTodo="deleteTodo"
	      :index="index"
	      :updataTodo="updataTodo"
	    />
	  </ul>
	</template>
	<script>
		import { defineComponent } from "vue";
		import Item from "./itemTo.vue"; // 因为要循环遍历,就把每一项单独拉出去做成组件 item
		export default defineComponent({
		  name: "listTo",
		  components: { Item },
		  props: ["todos", "deleteTodo", "updataTodo"], // 接收父组件app传递来的数据,在通过 list传递给item
		  setup() {
		    return {};
		  },
		});
	</script>
	<style scoped>
		.todo-mian {
		  margin-left: 0px;
		  border: 1px solid #ddd;
		  border-radius: 2px;
		  padding: 0px;
		}
		.todo-empty {
		  height: 40px;
		  line-height: 40px;
		  border-radius: 2px;
		  border: 1px solid #ddd;
		  padding-left: 5px;
		  margin-top: 10px;
		}
	</style>

Item 文件

<template>
<!-- 鼠标移入移除时事件和颜色改变 -->
  <li
    @mouseenter="mouseHander(true)"
    @mouseleave="mouseHander(false)"
    :style="{ backgroundColor: bgColor, color: whColor }"
  >
    <label>
      <input type="checkbox" v-model="isCompleted" />
      <span>{{ todo.title }}</span>
    </label>
    <!-- 删除按钮操作 -->
    <button
      class="btn btn-danger"
      style="display: none"
      v-show="isShow"
      @click="delTodo"
    >
      删除
    </button>
  </li>
</template>
<script>
	import { computed, defineComponent, ref } from "vue";
	export default defineComponent({
	  name: "itemTo",
	  props: {
	  // 父组件list传递的
	    todo: {
	      type: Object,
	      required: true,
	    },
	    deleteTodo: {
	      type: Function,
	      required: true,
	    },
	    index: {
	      type: Number,
	      required: true,
	    },
	    updataTodo: {
	      type: Function,
	      required: true,
	    },
	  },
	  setup(props) {
	    const bgColor = ref("white");
	    const whColor = ref("black");
	    const isShow = ref(false);
	    // 鼠标进入和离开的函数,flag true/false
	    const mouseHander = (flag) => {
	      if (flag) {
	        bgColor.value = "pink";
	        whColor.value = "green";
	        isShow.value = true;
	      } else {
	        bgColor.value = "white";
	        whColor.value = "black";
	        isShow.value = false;
	      }
	    };
	    // 删除数据方法
	    const delTodo = () => {
	      if (window.confirm("删除吗")) {
	        props.deleteTodo(props.index);
	      }
	    };
	    // 计算属性的方式,操作选中状态
	    const isCompleted = computed({
	      get() {
	        // 查询到传递过来的todo对象里面的数据的状态
	        return props.todo.isCompleted;
	      },
	      set(val) {
	        console.log(val);
	        props.updataTodo(props.todo, val);
	      },
	    });
	    return {
	      mouseHander,
	      bgColor,
	      whColor,
	      isShow,
	      delTodo,
	      isCompleted,
	    };
	  },
	});
</script>
<style scoped>
	li label li input {
	  vertical-align: middle;
	  margin-right: 6px;
	  position: relative;
	  top: -1px;
	}
	li button {
	  float: right;
	}
	li:before {
	  content: initial;
	}
	li:last-child {
	  border-bottom: none;
	}
</style>

底部的 footer 文件

<template>
  <div class="todo-footer">
    <label>
      <input type="checkbox" v-model="isCkeck" />
    </label>
    <span>
      <span>已完成{{ count }}</span> / 全部{{ todos.length }}
    </span >
    <button class="btn btn-danger" @click="deleteAll">清除完成任务项</button>
  </div>
</template>
<script>
import { defineComponent, computed } from "vue";
export default defineComponent({
  name: "foterTo",
  props: {
    todos: {
      type: Array,
      required: true,
    },
    checkAll: {
      type: Boolean,
      required: true,
    },
    deleteAll: {
      type: Function,
      required: true,
    },
  },
  setup(props) {
    console.log(props);
    // 已完的成计算属性
    const count = computed(() => {
      return props.todos.reduce(
        (pre, todo, index) => pre + (todo.isCompleted ? 1 : 0),
        0
      );
    });
    // 全选、全不选计算属性
    const isCkeck = computed({
      get() {
        return count.value > 0 && props.todos.length === count.value;
      },
      set(val) {
        props.checkAll(val);
        return;
      },
    });

    return {
      count,
      isCkeck,
    };
  },
});
</script>
<style scoped>
	.todo-footer {
	  height: 40px;
	  line-height: 40px;
	  padding-left: 6px;
	  margin-top: 5px;
	}
	.todo-footer label {
	  display: inline-block;
	  margin-right: 20px;
	  cursor: pointer;
	}
	.todo-footer label input {
	  position: relative;
	  top: -1px;
	  vertical-align: middle;
	  margin-right: 5px;
	}
	.todo-footer button {
	  float: right;
	  margin-top: 5px;
	}
</style>

就这样结束了。
如果不想这样来回的组件封装。在一个文件中直接展示,可以看下个文件

相关文章:

  • 前端——运用@media实现网页自适应中的几个关键分辨率
  • vue3 实现 todoList 知识点 reactive, toRefs, computed, watchEffect, filters,localStorage数据存储、获取
  • vue 项目 前端 模拟后端接口数据(vue2,vue3)
  • JSON.toJSONString的jar包问题
  • vue3-cli创建项目后每个文件的第一行都爆红,或者每个文建都 : No Babel config file detected for...
  • vue3路由传参 query 、params
  • Dom4j把xml转换成Map(非固定格式)
  • vue2 和 vue3 的 路由守卫
  • Android EventBus 3.0.0 使用总结
  • vue3 中vuex状态管理
  • 用过属性来给标签加样式
  • vue2和vue3 的 keep-alive的用法
  • php地址赋值值和传值赋值
  • vue3 Uncaught (in promise) TypeError: Cannot read properties of undefined 其实是解构赋值的问题
  • __new__方法
  • #Java异常处理
  • 【React系列】如何构建React应用程序
  • axios请求、和返回数据拦截,统一请求报错提示_012
  • ES2017异步函数现已正式可用
  • JavaScript的使用你知道几种?(上)
  • js算法-归并排序(merge_sort)
  • Linux学习笔记6-使用fdisk进行磁盘管理
  • mysql 数据库四种事务隔离级别
  • python 装饰器(一)
  • react-native 安卓真机环境搭建
  • Redis 懒删除(lazy free)简史
  • Webpack 4x 之路 ( 四 )
  • windows下mongoDB的环境配置
  • 如何打造100亿SDK累计覆盖量的大数据系统
  • 如何使用 JavaScript 解析 URL
  • 我是如何设计 Upload 上传组件的
  • Mac 上flink的安装与启动
  • MyCAT水平分库
  • ​Kaggle X光肺炎检测比赛第二名方案解析 | CVPR 2020 Workshop
  • # 20155222 2016-2017-2 《Java程序设计》第5周学习总结
  • #我与Java虚拟机的故事#连载04:一本让自己没面子的书
  • (C语言)输入一个序列,判断是否为奇偶交叉数
  • (附源码)ssm捐赠救助系统 毕业设计 060945
  • (附源码)计算机毕业设计SSM教师教学质量评价系统
  • (十) 初识 Docker file
  • (转)编辑寄语:因为爱心,所以美丽
  • ****** 二 ******、软设笔记【数据结构】-KMP算法、树、二叉树
  • .NET CORE Aws S3 使用
  • .NET 程序如何获取图片的宽高(框架自带多种方法的不同性能)
  • .NET的微型Web框架 Nancy
  • .NET简谈设计模式之(单件模式)
  • .net企业级架构实战之7——Spring.net整合Asp.net mvc
  • .net知识和学习方法系列(二十一)CLR-枚举
  • .net中应用SQL缓存(实例使用)
  • .Net转前端开发-启航篇,如何定制博客园主题
  • @manytomany 保存后数据被删除_[Windows] 数据恢复软件RStudio v8.14.179675 便携特别版...
  • [AIGC] 如何建立和优化你的工作流?
  • [Android]一个简单使用Handler做Timer的例子
  • [Android开源]EasySharedPreferences:优雅的进行SharedPreferences数据存储操作
  • [bzoj1324]Exca王者之剑_最小割