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

Vue(七) TodoList案例1.0

文章目录

  • 组件化编码流程(通用)
  • 1. 拆分静态组件
  • 2. 初始化列表
    • 展示动态数据
    • 如何让一个标签动态的拥有某一个属性
  • 3. 按回车添加todo
    • 子组件给父组件传值之props
  • 4. 勾选与取消勾选一个Todo
  • 5. 删除
  • 6. footer底部统计
  • 7. footer底部交互
    • 7.1 全选框自动打勾
    • 7.2 全选框取消勾选
  • 8. 清除已完成任务
  • 总结

组件化编码流程(通用)

1、实现静态组件:抽取组件,使用组件实现静态页面效果

2、展示动态数据:

  • 数据的类型、名称是什么
  • 数据保存再哪个组件

3、交互–从绑定事件监听开始

如果组件名字不好起,想一下是不是组件拆分的不合理

1. 拆分静态组件

根据功能,可划分为以下几个组件:
在这里插入图片描述
将已写好页面的html,css根据组件进行拆分,得到的文件结构如下:
App组件里使用了MyHeader、MyList、MyFooter组件
在这里插入图片描述
MyList组件里使用了MyItem组件:
在这里插入图片描述

2. 初始化列表

展示动态数据

(1) 确定数据名称、数据类型
数据名称:这一堆的代办事项数据名称可以叫todos,
数据类型:最好用数组存储这一堆的待办事项,而每一个代办事项可以用对象存储。

(2) 数据保存在哪个组件:
谁用这些数据,就写在哪个组件里后续如果有变化,再改变位置。因此暂时写在MyList组件里。
MyList.vue

<template><ul class="todo-main"><!-- 循环生成多个MyItem,且将具体的代办事项传递给MyItem,否则还是页面的列表里还是yyyy--><MyItem v-for="toboObj in todos" :key="toboObj.id" :todo="toboObj" /></ul>
</template>
<script>
...data () {return {todos: [{ id: '0001', title: '吃饭', done: true },{ id: '0002', title: '睡觉', done: true },{ id: '0003', title: '打豆豆', done: false }]}}
</script>

MyItem.vue

<template><li><label><!-- 添加checked属性,初始化复选框的数据 --><input type="checkbox" checked /><span>{{ todo.title }}</span></label><button class="btn btn-danger" style="display: none">删除</button></li>
</template><script>
export default {name: 'MyItem',// 接收MyList组件传递的对象props: ["todo"]
}
</script>

但是添加checked后,所有的复选框都被勾选了。

如何让一个标签动态的拥有某一个属性

答:v-bind动态绑定

  <input type="checkbox" :checked="todo.done" />

在这里插入图片描述

3. 按回车添加todo

输入框在Header组件中,因此Header组件中要添加点击事件

<template><div class="todo-header"><!-- v-model获取表单元素的值 --><input type="text" placeholder="请输入你的任务名称,按回车键确认" v-model="title" @keydown.enter="add"/></div>
</template>

add回调函数中要做以下几件事:

import { nanoid } from 'nanoid'
methods: {add () {// 1. 获取到表单元素的内容console.log(this.title); // 或者通过 e.target.value获取到输入框的内容// 2. 将用户的输入包装成一个todo对象const todoObj = { id: nanoid(), title: this.title, done: false }  // 3. 将todo对象添加到todo列表里,即todos数组中// 4. 输入框内容置空。this.title = '' // 或者 e.target.value = '' }
}
  • nanoid
    包装的对象应该有一个唯一的id,uuid这个库可以生成全球唯一的编码,但是太大且生成的编码复杂。采用uuid的精简版:nanoid

    安装npm i nanoid
    引用 import {nanoid} from 'nanoid'
    使用: nanoid()(这是个函数,直接调用即可)

  • todoObj在MyHeader组件里,而数据todos在MyList组件里。两组件为兄弟组件,目前还不会兄弟组件之间通信。所以还是采用props属性。
    为了方便MyHeader通信,将todos改放在App组件里,而不是MyList组件里。此时:
    在这里插入图片描述

App可通过props将todos交给MyList组件进行展示。MyHeader该如何将todoObj交给父组件App呢?

子组件给父组件传值之props

父组件给子组件传递带参的函数

<!--App.vue-->
<template>...<!-- 给子组件传函数 --><MyHeader :addTodo="addTodo"></MyHeader><!-- 给子组件传数据 --><MyList :todos="todos"></MyList><MyFooter></MyFooter>...
</template>
<script>methods: {// 添加todoObjaddTodo (todoObj) {this.todos.unshift(todoObj)}}
}
</script>

子组件接收函数

<!--MyHeader.vue-->
<script>props: ["addTodo"],methods: {add () {//完善一下:校验数据if (!this.title.trim()) return alert('输入不能为空')const todoObj = { id: nanoid(), title: this.title, done: false }// 调用函数,通过参数将todoObj传递给父组件this.addTodo(todoObj)this.title = ''}}
}
</script>

总结:子组件向父组件传值时,父组件需提前向子组件传递一个带参的函数,子组件通过函数的参数将数据传给父组件。

捋顺一下流程:
1、按下回车,执行MyHeader里的add函数,在add里调用了App组件里的addTodo函数。
2、addTodo函数修改了App组件中data里的todos数据。
3、Vue捕获到todos变了,于是重新解析App里的template模板,重新解析时,将变化后的todos交给了MyList组件。
4、MyList收到数据,重新解析自己组件内的模板,v-for,虚拟DOM对比,更新页面。

4. 勾选与取消勾选一个Todo

在这里插入图片描述

MyItem.vue添加change事件(@click也可以)

  <input  type="checkbox"  :checked="todo.done" @change="handleCheck(todo.id)" />

handleCheck事件里需要对todos数据进行修改。数据在App组件里, 数据在哪里,操作数据的方法就在哪里,所以这里需要把待修改的todo的id传递给App。子组件给父组件传值,父组件需要先传递一个带参函数。此处应该是App传给MyList,MyList再传递给MyItem。

App.vue

<template>
...<!-- 先将函数传递给MyList --><MyList :todos="todos" :checkTodo="checkTodo"></MyList>
...
<script>// 勾选or取消勾选一个todocheckTodo (id) {this.todos.forEach((todo) => {if (todo.id === id) {todo.done = !todo.done}})}
</script>

MyList.vue

  <MyItem   v-for="toboObj in todos" :key="toboObj.id" :todo="toboObj"  :checkTodo="checkTodo" />
<script>props: ["todos", "checkTodo"]
</script>

MyItem.vue

// 接收MyList组件传递的对象
props: ["todo", "checkTodo"],methods: {handleCheck (id) {this.checkTodo(id)}
}

需要注意的是:
:checked="todo.done" ----初始化复选框
@change="handleCheck(todo.id) ----更新复选框
这两个操作可用v-model进行合并

<!-- 这行代码也能实现,但是有点儿违反原则,因为这样修改了props里数据的值,不报错是因为,todo是个对象,地址值没被修改, --> 
<input type="checkbox" v-model="todo.done" />

5. 删除

在这里插入图片描述
点击按钮,获取当前MyItem的id,根据id删除该条数据。同样的套路

添加监听事件,监听事件里调用父组件传递过来的函数。通过父组件里的这个函数对todos进行删除操作。
App.vue:

<MyList :todos="todos" :checkTodo="checkTodo" :deleteTodo="deleteTodo"></MyList>
<script>// 删除一个tododeleteTodo (id) {this.todos = this.todos.filter((todo) => {return todo.id !== id})}
</script>

MyList.vue:

<MyItem
v-for="toboObj in todos"
:key="toboObj.id"
:todo="toboObj"
:checkTodo="checkTodo"
:deleteTodo="deleteTodo"
/>
<script>props: ["todos", "checkTodo", "deleteTodo"]
</script>

MyItem.vue:

<button class="btn btn-danger" @click="handleDelete(todo.id)">删除</button>
<script>handleDelete (id) {// 弹框if (confirm('确定要删除吗?')) {// 通知App,删除对应的todothis.deleteTodo(id)}}
</script>

在这里插入图片描述

6. footer底部统计

在这里插入图片描述

<!--Footer.vue--> 
<!--使用计算属性--> 
<span>已完成{{ doneTotal }}</span> / 全部{{ total }}
<script>props: ["todos"],computed: {total () {return this.todos.length},doneTotal () {// reduce记数return this.todos.reduce((prev, cur) => {return prev + (cur.done ? 1 : 0)}, 0)// 简写// return this.todos.reduce((prev, current) => prev + (current.done ? 1 : 0))}
</script>

7. footer底部交互

这里结合6底部统计一起看

7.1 全选框自动打勾

如果代办事项全都选上,则该框自动打勾,
在这里插入图片描述

<input type="checkbox" :checked="isAll" @change="checkAll" />
<script>computed: {isAll () {// 已完成的数量是否等于todos的总数return this.doneTotal === this.total}},
</script>  

但是这样有个问题:
在这里插入图片描述
改进:

isAll () {return this.doneTotal === this.total && this.total > 0
}

优化:当没有代办事项时,footer组件不应该显示
在这里插入图片描述

7.2 全选框取消勾选

方式一:添加点击事件
App组件中创建操作todos的函数并传递给子组件
在这里插入图片描述
子组件接收函数并调用
在这里插入图片描述
方式二:v-model
(注意此处与 第4节的勾选与取消勾选一个Todo的区别)

<!-- :check = "isAll"用于初始化复选框
@change="checkAll"用来更新复选框
-->
<input type="checkbox" :checked="isAll" @change="checkAll" />
<!-- 简写为 -->
<input type="checkbox" v-model="isAll" />

由于isAll是计算属性,且v-model是双向绑定,所以当操作复选框时,对isAll的值进行了修改;
在这里插入图片描述
所以此时isAll不能采用简写形式了

isAll: {get () {return this.doneTotal === this.total && this.total > 0},set (value) {this.checkAllTodo(value)}
}

8. 清除已完成任务

在这里插入图片描述

添加点击事件
MyFooter.vue

<button class="btn btn-danger" @click="clearAll">清除已完成任务</button>
<script>
props: ["todos", "checkAllTodo", "clearAllTodo"],
clearAll () {this.clearAllTodo()
}
</script>

App.vue

<MyFooter:todos="todos":checkAllTodo="checkAllTodo":clearAllTodo="clearAllTodo"
></MyFooter>
<script>
// 清除所有已完成的todoclearAllTodo () {this.todos = this.todos.filter((todo) => {return !todo.done})}
</script>

总结

  1. 组件化编码流程:
  • 拆分静态组件:组件要按照功能点拆分,命名不要与html元素冲突。
  • 实现动态组件:考虑好数据的存放位置,数据是一个组件在用,还是一些组件在用
    • 一个组件在用:放在组件自身即可。
    • 一些组件在用:放在他们共同的父组件上(状态提升
  • 实现交互:从绑定事件开始。数据在哪里,对数据进行处理的方法就在哪里
  1. props适用于:

    ​ (1).父组件 ==> 子组件 通信

    ​ (2).子组件 ==> 父组件 通信(要求父先给子一个函数)

  2. 使用v-model时要切记:v-model绑定的值不能是props传过来的值,因为props是不可以修改的!

  3. props传过来的若是对象类型的值,修改对象中的属性时Vue不会报错,但不推荐这样做。

项目完整代码链接:https://gitee.com/LXHST/vue2–Todo-list-case-1.0/tree/master/

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 新的Ubuntu服务器如何启用root账号和配置静态ip以及开启ssh服务
  • spark.sql
  • 数仓基础(九):各大公司实时数仓实践
  • Spring Security与Apache Shiro:Java安全框架的比较
  • 电信500M宽带+AX210无线网卡测速
  • charles使用ssl证书抓包https请求失败解决方案
  • python-简单的dos攻击
  • Flutter之CRC校验
  • Unity编辑器开发 Immediate Mode GUI (IMGUI)
  • 多目标应用:基于多目标雾凇算法(MORIME)的移动机器人路径规划研究(提供MATLAB代码)
  • 电脑办公之基础操作(持续更新)
  • 帆软报表使用url访问报表,自定义前端搜索,优化报表展示
  • 使用GPU加速及配置
  • java反射:动态修改注解上属性的值
  • java 实现的单例,在static块中实例化是否会有线程安全问题?
  • 0x05 Python数据分析,Anaconda八斩刀
  • bootstrap创建登录注册页面
  • EventListener原理
  • Java,console输出实时的转向GUI textbox
  • Javascript弹出层-初探
  • miniui datagrid 的客户端分页解决方案 - CS结合
  • Next.js之基础概念(二)
  • ⭐ Unity 开发bug —— 打包后shader失效或者bug (我这里用Shader做两张图片的合并发现了问题)
  • 从零开始的webpack生活-0x009:FilesLoader装载文件
  • 大快搜索数据爬虫技术实例安装教学篇
  • 给github项目添加CI badge
  • 如何优雅地使用 Sublime Text
  • 好程序员web前端教程分享CSS不同元素margin的计算 ...
  • ​ ​Redis(五)主从复制:主从模式介绍、配置、拓扑(一主一从结构、一主多从结构、树形主从结构)、原理(复制过程、​​​​​​​数据同步psync)、总结
  • !$boo在php中什么意思,php前戏
  • #我与Java虚拟机的故事#连载12:一本书带我深入Java领域
  • (16)Reactor的测试——响应式Spring的道法术器
  • (C语言)求出1,2,5三个数不同个数组合为100的组合个数
  • (delphi11最新学习资料) Object Pascal 学习笔记---第8章第2节(共同的基类)
  • (超详细)2-YOLOV5改进-添加SimAM注意力机制
  • (附源码)ssm高校志愿者服务系统 毕业设计 011648
  • (紀錄)[ASP.NET MVC][jQuery]-2 純手工打造屬於自己的 jQuery GridView (含完整程式碼下載)...
  • (十)c52学习之旅-定时器实验
  • (五)关系数据库标准语言SQL
  • (一)u-boot-nand.bin的下载
  • (转)memcache、redis缓存
  • *_zh_CN.properties 国际化资源文件 struts 防乱码等
  • .NET Framework、.NET Core 、 .NET 5、.NET 6和.NET 7 和.NET8 简介及区别
  • .net 发送邮件
  • .NET/C# 的字符串暂存池
  • @entity 不限字节长度的类型_一文读懂Redis常见对象类型的底层数据结构
  • @NoArgsConstructor和@AllArgsConstructor,@Builder
  • @Value获取值和@ConfigurationProperties获取值用法及比较(springboot)
  • [ vulhub漏洞复现篇 ] GhostScript 沙箱绕过(任意命令执行)漏洞CVE-2019-6116
  • [.NET 即时通信SignalR] 认识SignalR (一)
  • [AX]AX2012开发新特性-禁止表或者表字段
  • [BZOJ4016][FJOI2014]最短路径树问题
  • [C#]科学计数法(scientific notation)显示为正常数字
  • [C/C++入门][ifelse]20、闰年判断
  • [CF494C]Helping People