Vue2(五)待办事项todo-list小案例

2024-02-14 15:59

本文主要是介绍Vue2(五)待办事项todo-list小案例,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

(一)分析结构

(二)搭建静态组件

(三)实现功能

1.动态初始化数据

2.添加待办事项

3.勾选的双向绑定

(1)v-model绑定

 (2)最初级的绑定

4.删除功能

5.底部统计与交互功能

(1)底部统计功能

(2)交互功能

6.数据存储优化:本地存储

7.使用组件自定义事件升级一下

8.使用全局事件总线升级一下

9.拓展:新增一个修改事项名的功能

(四)小总结 


(一)分析结构

 可大致分为三个结构

(1)输入组件(2)待办事项组件(3)footer组件

其中待办事项组件又可以分为一个一个的事项item组件

代码顺序应该是:先编写组件基本结构,将静态界面搭建出来,再依次完成数据渲染、输入添加功能、勾选功能、删除功能、底部统计功能和交互功能等。

(二)搭建静态组件

拆分的四个组件进行基本的写入

随便写的css样式,此时所有功能还都不能实现 

 

(三)实现功能

1.动态初始化数据

将数据以对象数组形式存储在ListTodos组件中,通过props配置读取数据完成items信息的展示

使用v-for循环写入

在ListTodos.vue中:

// 数据
data() {return {todos: [{ id: '001', title: '打代码', done: false },{ id: '002', title: '睡觉', done: true },{ id: '003', title: '吃饭', done: false }]}},// v-for循环渲染组件
<div id="todos"><TodosItem v-for="todoObj in todos" :key="todoObj.id" :todo="todoObj" />
</div>

需要注意的是,将todo传入组件TodosItem中需要用v-bind绑定,这样引号内就不是字符串而是一个表达式,即成功传入一个对象todoObj

在TodosItem.vue中:

<div class="item"><input type="checkbox" name="selectOne" :checked="todo.done"><span>{{ todo.title }}</span><button>删除</button>
</div>//定义props配置 todo
props: ['todo']

复选框是否勾选的状态由传入的对象中的done来决定,同样的,使用v-bind绑定后可读取引号内的表达式

剩余的根据复选框来修改todoObj的数据操作后续再完成;待办事项数据的存储位置也后续再优化

2.添加待办事项

输入框输入事项后按enter触发添加事件,但是目前还没有学到同级之间的组件如何进行数据传输,所以转换一个思路,把数据放到父组件,这样两个子组件都能访问到

App.vue文件

<div id="app"><!-- 将添加对象函数传入 在header文件内调用该函数实现父子的连接 --><ListHeader :addObj="addObj" /><!-- 将数据传入 --><ListTodos :todos="todoObjs" /><ListFooter />
</div>data() {return {todoObjs: [{ id: '001', title: '打代码', done: false },{ id: '002', title: '睡觉', done: true },{ id: '003', title: '吃饭', done: false }]}},
methods: {// 将header文件内新生成的数组加入到父组件app.vue数据中addObj(obj) {this.todoObjs.unshift(obj)}},

ListTodos文件

利用props属性接收对象数组后直接使用即可:props: ['todos']

ListHeader文件

<header><input type="text" v-model="title" @keyup.enter="add"placeholder="输入待办事项,按enter键添加">
</header>// 引入nanoid库
import { nanoid } from 'nanoid'//...data() {return {title: ''}},methods: {// 添加函数 将事项名和id、完成状态一起打包成对象再加入到todoObj中add() {if (this.title === '') returnconst Obj = { id: nanoid(), title: this.title, done: false }this.addObj(Obj)this.title = '' //清零}},// 接收函数props: ['addObj']

nanoid库用于生成一个独一无二的字符串作为id使用

引入:import { nanoid } from 'nanoid' 调用:nanoid() // 返回一个字符串

数组也可以通过props属性直接传到子组件里面用!!!

传入的数组实际this指针还是指向父组件,这样就实现了父组件和子组件的数据交互

除了使用v-model绑定事项的title,还可以用e.target.value 

3.勾选的双向绑定

实现待办事项done属性的勾选双向数据绑定

(1)v-model绑定

下面这种v-model方式实现的数据绑定是大错特错的!!!

这样写v-model="todo.done"违反了props只读的规定,只不过在对象内的修改不会修改对象的地址,所以不会引起vue的报错 

 

 (2)最初级的绑定

还是和添加一条对象的方式一样,通过点击复选框触发事件,向app.vue传递被点击复选框所对应的id,再在app组件中直接修改对应id的对象的done状态(取反即可)

只不过这样较为麻烦,爷爷组件没办法直接传递checkObj函数给孙子组件,只能先传给listTodos组件,再传递给item组件,这是最基础的写法,后面再学高级的

在App.vue中

<!-- 将数据传入 -->
<ListTodos :todos="todoObjs" :checkObj="checkObj" />// 将item文件内查找到的点击的复选框对应的事项对象的id传给爷爷组件app中,进行修改完成状态的操作checkObj(id) {this.todoObjs.forEach((todo) => {if (id === todo.id) {todo.done = !todo.doneconsole.log(this);}})}

在ListTodos文件中

只需要props传递checkObj函数即可

在TodosItem文件中

<input type="checkbox" :checked="todo.done" @click="check(todo.id)">props: ['todo', 'checkObj'],methods: {check(id) {this.checkObj(id)}},

4.删除功能

(1)通过css实现鼠标经过时高亮,删除键display:block

(2)点击删除按钮触发事件删除该对应事项对象的数据

和上面的checkObj函数相似,需要在item文件获取触发对应事项的id,再依次传递给爷爷App.vue

在app文件中编写对应的删除函数操作

这里有两个删除方法

1.通过filter函数过滤传入id对应的对象,将剩余对象组成一个新的数组赋值给this.todoObjs

deleteObj(id) {// 1.filter方法生成新数组,再覆盖this.todoObjs = this.todoObjs.filter((todo) => {return todo.id != id})
}

2.通过数组删除函数splice(index,1),和findIndex()找到传入id对应的数组索引下标进行删除

deleteObj(id) {// 2.splice方法:找到对应数组的下标findindex再删除this.todoObjs.splice(this.todoObjs.findIndex((todo) => {return todo.id === id}), 1)
}

5.底部统计与交互功能

(1)底部统计功能

统计已完成数量和总数量

从app把todoObjs数组传入footer文件 

总数量就是传入的todos的长度;todos.length 

已完成数量可以通过computed计算,用foreach来计算完成事项的数量

computed: {total() {return this.todos.length},// 用于计算已完成事项的数量finish() {// 基础方法// let num = 0// this.todos.forEach(todo => {//     if (todo.done) num++// })// return num// reduce方法return this.todos.reduce((pre, current) => {return pre + (current.done ? 1 : 0)}, 0)}
},

也可以使用reduce方法更高级更简单;reduce((pre,current)=>{},0)

0是pre的初始值,pre是每次递归的值(显示上一次的值),current是当前对象 

(2)交互功能

1.实现勾选后每个事项全部勾选;取消勾选后每个事项全部取消勾选,即跟着总的勾选情况来改动;

在ListFooter文件 

<input type="checkbox" @click="selectAll">selectAll(e) {// 调用将所有对象的done状态根据复选框的状态而改变this.selectObjs(e.target.checked)
},

 在App.vue文件

// 将所有对象的done属性根据复选框的状态变化
selectObjs(value) {this.todoObjs.forEach(todo => {todo.done = value})
},

2.点击全部勾选后,如果取消单个事项的勾选,则全部勾选要取消;若所有单个事项都勾选了,则全部勾选也要自动勾选上,即根据底部统计的已完成和全部数量来判断

<input type="checkbox" @click="selectAll" :checked="finish === todos.length && todos.length != 0">

只需将checked属性双向绑定即可::checked="finish === all && all != 0"

这样将表达式写在标签内看着太繁复了,所以写在计算属性里面才是正解

// 判断已完成事项数量是否是全部数量
isAll() {return this.finish === this.total && this.total != 0
}

也可以使用v-model="isAll"来绑定,和:checked效果一样,因为v-model的绑定值就是checked;

不过还需要给计算属性isAll加上一个setter不然会报错

 还可以让功能更完善点儿,待办事项为0的时候底部可以不显示(没有任何意义)

        v-show="total"即可

3.点击清除按钮后实现勾选的事项全部删除

只需要在footer函数写一个按钮触发事件,在app文件中写好clean函数即可

// 将所有对象中完成的对象一键删除 可复用删除函数
cleanObj() {this.todoObjs.forEach(todo => {if (todo.done) {// 这里不能用splice的删除方法,每次删掉一个对象后整个数组的下标也跟着改变了this.deleteObj(todo.id)}})
}

复用deleteObj函数实现删除功能

注意!!!不能用上文写到的splice删除方法!

因为每次删掉一个对象后整个数组的下标也都跟着改变了,会出现没办法删干净有遗漏的情况!

6.数据存储优化:本地存储

在App.vue文件内:

使用监视属性,深度监视存储数据的对象数组,一旦发生改变(增加、删除、完成状态改变)就会触发本地存储更新为与todoObjs对象数组一致;todoObjs对象数组的首次读取是读取本地存储的数据,若没有该数据则返回一个空数组

data() {return {todoObjs: JSON.parse(localStorage.getItem('todoObjs')) || []}},watch: {todoObjs: {deep: true,handler(value) {localStorage.setItem('todoObjs', JSON.stringify(value))}}}

7.使用组件自定义事件升级一下

在app.vue中:

<!-- 组件自定义事件 在header文件里触发该事件则调用函数addObj 子传父-->
<ListHeader @addObj="addObj" /><!-- 组件自定义事件 子传父 todoObjs是父传子的数据,不需要自定义事件 -->
<ListFooter :todos="todoObjs" @selectObjs="selectObjs" @cleanObj="cleanObj" />

和直接用props进行父子通信也差不多

8.使用全局事件总线升级一下

在app.vue中:

mounted() {// 绑定自定义事件this.$bus.$on('checkObj', this.checkObj)this.$bus.$on('deleteObj', this.deleteObj)
},
beforeDestroy() {// 解绑this.$bus.$off('checkObj')this.$bus.$off('deleteObj')
},

在todositem中:

methods: {check(id) {this.$bus.$emit('checkObj', id)},deleteTodo(id) {this.$bus.$emit('deleteObj', id)}
},

使用全局总线就可以实现爷孙通信了,不必再通过item的父组件传递props;推荐使用 

9.拓展:新增一个修改事项名的功能

 

 实现点击修改后出现输入框,写入新名字失去焦点后将名字更新显示

在todosItem中:

<span v-show="!isEdited">{{ todo.title }}</span>
<input type="text" @blur="blurTodo(todo, $event)" :value="todo.title" v-show="isEdited" ref="focusIpt"><button v-show="!isEdited" @click="editTodo">修改</button>
data() {return {isEdited: false}
},
methods: {editTodo() {this.isEdited = true// vue在所有处理完后才会开始编译模板,如果直接写聚焦,此时模板还未编译,输入框还未出现,执行聚焦也没有效果// $nextTick实现模板更新编译后再执行内部的代码this.$nextTick(function () {this.$refs.focusIpt.focus()})// 也可以使用计时器 达到一个异步处理的效果// setTimeout(() => {//     this.$refs.focusIpt.focus()// },);},blurTodo(todo, e) {this.isEdited = falsethis.$bus.$emit('editObj', todo.id, e.target.value)}
},

 在item组件中新添加一个数据isEdited来判断该组件是否处于修改状态;

在App.vue中:

mounted() {// 绑定自定义事件this.$bus.$on('editObj', (id, title) => {// 修改为空之后取消修改if (!title.trim()) return alert("不能为空!")this.todoObjs.forEach((todo) => {if (todo.id == id) todo.title = title})})
},
beforeDestroy() {// 解绑this.$bus.$off('editObj')
},

(四)小总结 

现实中一个这样的案例不需要拆成很多个小组件,一个就搞定,不过为了更好的了解学习小知识点进行一个拆分是很有意义的。通过这个案例,编写组件的思路更加清晰,一步一步循序渐进,感觉还不错。

学到了很多细碎的知识点。

1.目前还没有学到事件总线,所以同级组件之间的数据交互是通过最基础的方式:数据放在父组件,再将数据传递给子组件;同时学到了重要的一点:父组件的函数也可以通过props属性传递给子组件,从而通过操作子组件实现父组件数据的交互

2.复习了很多es6的函数(可见我对es6很不熟悉..),关于数组操作的forEach()、filter()、reduce(),数组的增删函数:unshift()、splice()等

3.还了解了一个组件库nanoid,用于生成一个独一无二字符串id

4.v-model操作复选框实际操作的不是value而是checked

5.props属性传过来的值是只读的!!如果需要修改就在data另外保存,不能直接操作这个值!!否则会报错!如果操作的是对象数组则有可能vue监测不到改变,因为地址没变,但是仍然是不建议的,要通过子组件修改父组件的数据建议是通过父组件传给子组件的函数进行修改

6.本地存储webStorage

        localStorage:网页关闭保存的数据不会消失,需要手动删除

        sessionStorage:网页关闭则保存的数据消失 

7.组件通信的方式选择

        父子间的通信最好采用props或者自定义事件,不必采用全局事件总线大费周章

        同级组件、爷孙组件的通信使用全局总线更加方便 

这篇关于Vue2(五)待办事项todo-list小案例的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/708954

相关文章

Vue3 的 shallowRef 和 shallowReactive:优化性能

大家对 Vue3 的 ref 和 reactive 都很熟悉,那么对 shallowRef 和 shallowReactive 是否了解呢? 在编程和数据结构中,“shallow”(浅层)通常指对数据结构的最外层进行操作,而不递归地处理其内部或嵌套的数据。这种处理方式关注的是数据结构的第一层属性或元素,而忽略更深层次的嵌套内容。 1. 浅层与深层的对比 1.1 浅层(Shallow) 定义

这15个Vue指令,让你的项目开发爽到爆

1. V-Hotkey 仓库地址: github.com/Dafrok/v-ho… Demo: 戳这里 https://dafrok.github.io/v-hotkey 安装: npm install --save v-hotkey 这个指令可以给组件绑定一个或多个快捷键。你想要通过按下 Escape 键后隐藏某个组件,按住 Control 和回车键再显示它吗?小菜一碟: <template

【 html+css 绚丽Loading 】000046 三才归元阵

前言:哈喽,大家好,今天给大家分享html+css 绚丽Loading!并提供具体代码帮助大家深入理解,彻底掌握!创作不易,如果能帮助到大家或者给大家一些灵感和启发,欢迎收藏+关注哦 💕 目录 📚一、效果📚二、信息💡1.简介:💡2.外观描述:💡3.使用方式:💡4.战斗方式:💡5.提升:💡6.传说: 📚三、源代码,上代码,可以直接复制使用🎥效果🗂️目录✍️

Hadoop企业开发案例调优场景

需求 (1)需求:从1G数据中,统计每个单词出现次数。服务器3台,每台配置4G内存,4核CPU,4线程。 (2)需求分析: 1G / 128m = 8个MapTask;1个ReduceTask;1个mrAppMaster 平均每个节点运行10个 / 3台 ≈ 3个任务(4    3    3) HDFS参数调优 (1)修改:hadoop-env.sh export HDFS_NAMENOD

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

性能分析之MySQL索引实战案例

文章目录 一、前言二、准备三、MySQL索引优化四、MySQL 索引知识回顾五、总结 一、前言 在上一讲性能工具之 JProfiler 简单登录案例分析实战中已经发现SQL没有建立索引问题,本文将一起从代码层去分析为什么没有建立索引? 开源ERP项目地址:https://gitee.com/jishenghua/JSH_ERP 二、准备 打开IDEA找到登录请求资源路径位置

深入探索协同过滤:从原理到推荐模块案例

文章目录 前言一、协同过滤1. 基于用户的协同过滤(UserCF)2. 基于物品的协同过滤(ItemCF)3. 相似度计算方法 二、相似度计算方法1. 欧氏距离2. 皮尔逊相关系数3. 杰卡德相似系数4. 余弦相似度 三、推荐模块案例1.基于文章的协同过滤推荐功能2.基于用户的协同过滤推荐功能 前言     在信息过载的时代,推荐系统成为连接用户与内容的桥梁。本文聚焦于

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

【区块链 + 人才服务】可信教育区块链治理系统 | FISCO BCOS应用案例

伴随着区块链技术的不断完善,其在教育信息化中的应用也在持续发展。利用区块链数据共识、不可篡改的特性, 将与教育相关的数据要素在区块链上进行存证确权,在确保数据可信的前提下,促进教育的公平、透明、开放,为教育教学质量提升赋能,实现教育数据的安全共享、高等教育体系的智慧治理。 可信教育区块链治理系统的顶层治理架构由教育部、高校、企业、学生等多方角色共同参与建设、维护,支撑教育资源共享、教学质量评估、