文章目录

  • 界面完成
  • 添加功能实现
  • 勾选
    • 实现方法一
    • 实现方法二
  • 删除
  • 底部统计功能实现
  • 底部清除已完成任务
  • 总结

界面完成


components 文件夹下新建 Header.vue来完成头部组件

<template><div class="todo-header"><input type="text" placeholder="请输入任务名,按回车确认"></div>
</template><script>
export default {name: "Header"
}
</script><style scoped>
.todo-header input {width: 560px;height: 28px;font-size: 14px;border: 1px solid #ccc;border-radius: 4px;padding: 4px 7px;
}.todo-header input:focus{outline: none;border-color: rgba(82,168,236,0.8);box-shadow: inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.8);
}</style>

Footer.vue 来完成底部组件

<template><div class="todo-footer"><label><input type="checkbox"></label><span><span>已完成0</span>/全部2</span><button class="btn btn-danger">清除已完成任务</button></div>
</template><script>
export default {name: "Footer"
}
</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>

List.vue 来完成中间内容组件

<template><div><ul class="todo-main"><Item v-for="todoObj in todos" :key="todoObj.id" :todo="todoObj"/></ul></div>
</template><script>
import Item from "./Item"export default {name: "List",components: {Item},data(){return{todos:[{id:'001',title:'抽烟',done:true},{id:'002',title:'喝酒',done:false},{id:'003',title:'开车',done:true},]}}
}
</script><style scoped>
.todo-main {margin-left: 0px;border: 1px solid #ddd;border-radius: 2px;padding: 0px;
}
.todo-empty {height: 40px;line-height: 40px;border: 1px solid #ddd;border-radius: 2px;padding-left: 5px;margin-top: 10px;
}
</style>

Item.vue 来完成每一项的组件

<template><li><label><input type="checkbox" :checked="todo.done"><span>{{todo.title}}</span></label><button class="btn btn-danger" style="display:none">删除</button></li>
</template><script>
export default {name: "Item",props:['todo']
}
</script><style scoped>
li {list-style: none;height: 36px;line-height: 36px;padding: 0 5px;border-bottom: 1px solid #ddd;
}li label {float: left;cursor: pointer;
}li label li input {vertical-align: middle;margin-right: 6px;position: relative;top: -1px;
}li button {float: right;display: none;margin-top: 3px;
}li:before {content: initial;
}li:last-child {border-bottom: none;
}</style>

App.vue 中引入并注册组件,同时写一些公共样式

<template><div class="root"><div class="todo-container"><div class="todo-wrap"><Header/><List/><Footer/></div></div></div>
</template><script>
//引入组件
import Header from "./components/Header";
import Footer from "./components/Footer";
import List from "./components/List";export default {name: 'App',components: {Header,Footer,List}
}
</script><style>
body {background: #ffffff;
}.btn {display: inline-block;padding: 4px 12px;margin-bottom: 0;font-size: 14px;line-height: 20px;text-align: center;vertica1-align: middle;cursor: pointer;box-shadow: inset 0 1px 0 rgb(255, 255, 255, 0.2), 0 1px 2px rgb(255, 255, 255, 0.5);border-radius: 4px;
}.btn-danger {color: #ffffff;background-color: #da4f49;border: 1px solid #bd362f;
}.btn-danger:hover {color: #ffffff;background-color: #bd362f;
}.btn:focus {outline: none;
}.todo-container {width: 600px;margin: 0 auto;
}.todo-container .todo-wrap {padding: 10px;border: 1px solid #ddd;border-radius: 5px;
}
</style>

main.js

//引入Vue
import Vue from 'vue';
//引入App
import App from './App';//关闭vue的生产提示
Vue.config.productionTip = false//创建vm
new Vue({el: "#app",render: h => h(App)
})

添加功能实现

我们需要在 Header.vue 编写代码,首先给 input 中增加键盘抬起时的事件 add,当按下回车时获取输入内容,然后包装成一个对象,也就是 List.vue 中相同的数据类型{id:'001',title:'抽烟',done:true}

其中 id 需要是唯一标识,可以使用 npm i nanoid 来安装 nanoid,用来生成唯一 id
其中 title 就是输入的内容,通 add(e) 中的参数 e.target.value 就可以拿到
最后 done 默认 false

<template><div class="todo-header"><input type="text" placeholder="请输入任务名,按回车确认" @keyup.enter="add"></div>
</template><script>
import {nanoid} from 'nanoid'
export default {name: "Header",methods: {add(e) {const todoObj = {id: nanoid(), title: e.target.value, done: false}console.log(todoObj);}//或者在input中增加双向绑定v-model="title"/*add(){console.log(this.title);}*/}
}
</script><style scoped>
.todo-header input {width: 560px;height: 28px;font-size: 14px;border: 1px solid #ccc;border-radius: 4px;padding: 4px 7px;
}.todo-header input:focus {outline: none;border-color: rgba(82, 168, 236, 0.8);box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.8);
}</style>

在头部输入123,然后按回车,查看打印的内容

好了现在需要把这个对象添加给 List.vue 中的 todos 中。我们之前学过父组件向子组件中传值,但目前没有学兄弟组件之间的传值,所以,我们可以把 List.vue 中的数据 todos 剪切走放到 App.vue 中(后边学习其他方法)

App.vue 中

......
<List :todos="todos"/>
......  <script>
//引入组件
......export default {name: 'App',components: {......},data(){return{todos:[{id:'001',title:'抽烟',done:true},{id:'002',title:'喝酒',done:false},{id:'003',title:'开车',done:true},]}}
}
</script>

然后 List.vue 中接收一下就可以了

......
<script>
import Item from "./Item"export default {name: "List",props:["todos"],components: {Item}
}
</script>

接着,当 Header.vue 中组装了一个对象后,传给 App.vue,需要这样做:首先 App.vue 中需要先传一个方法 addTodo 给 Header.vue,然后 Header.vue 接收到这个方法调用即可

修改 App.vue

<Header :addTodo="addTodo"/><script>
......
export default {......methods:{addTodo(todoObj){this.todos.unshift(todoObj)}}
}
</script>

修改 Header.vue

......
<script>
......
export default {......props:["addTodo"],methods: {add(e) {if(!e.target.value.trim()) return alert("输入不能为空")const todoObj = {id: nanoid(), title: e.target.value, done: false}this.addTodo(todoObj)e.target.value = ""}//或者在input中增加双向绑定v-model="title"/*add(){console.log(this.title);}*/}
}
</script>
......

勾选

实现方法一

思路:勾选后需要改变数据中的 done 的值,任务列表 todos 在 App.vue 中,所以在 App.vue 中增加 checkTodo 方法。由于 App.vue - List.vue - Item.vue 是嵌套关系,我们需要逐层传递,因此 先把这个方法传给 List.vue

App.vue

<List :todos="todos" :checkTodo="checkTodo"/><script>
......
export default {......methods:{......//勾选或取消勾选一个todocheckTodo(id){this.todos.forEach((todo)=>{if(todo.id === id) todo.done = !todo.done})}}
}
</script>

List.vue 中接收并再传递给 Item.vue


<Item v-for="todoObj in todos" :key="todoObj.id" :todo="todoObj" :checkTodo="checkTodo"/><script>
......export default {......props:["todos","checkTodo"],......
}
</script>
......

Item.vue 中接收调用,在 checkbox 中增加 change 事件,获取选中或取消勾选的 id


<input type="checkbox" :checked="todo.done" @change="handleCheck(todo.id)"><script>
export default {name: "Item",props:['todo','checkTodo'],methods:{handleCheck(id){this.checkTodo(id)}}
}
</script>

到此为止我们就实现了勾选改变值的功能,运行看下效果:

实现方法二

我们可以换另一种写法,v-model对应的 data 属性是 Boolean 时,返回的是checkboxchecked的 boolean 值,所以第一种方法中的 传方法之类的全都不写了,直接修改 Item.vue

<input type="checkbox" v-model="todo.done">

虽然同样可以实现上边的效果,但是这种方法是不建议的,因为我们之前说过,传过来的 props 是不能修改的,为什么 vue 还没报错呢?

我们看着段代码

let obj = {a:1,b:2}
obj.a = 2//第一种修改方式
obj = {x:11,b:22}//第二种修改方式

vue 能检测到的是第二种修改方式

不建议这种写法哦

删除


先修改 App.vue,增加一个删除方法 deleteTodo,传给 List.vue

<template><div class="root"><div class="todo-container"><div class="todo-wrap"><Header :addTodo="addTodo"/><List :todos="todos" :checkTodo="checkTodo" :deleteTodo="deleteTodo"/><Footer/></div></div></div>
</template><script>
......export default {......methods: {......//删除tododeleteTodo(id) {//过滤不改变原数组/*this.todos = this.todos.filter((todo)=>{return todo.id !== id})*///可以精简为this.todos = this.todos.filter(todo => todo.id !== id)}}
}
</script>
......

List.vue 接收,并传给 Item.vue

<template><div><ul class="todo-main"><Item ......:deleteTodo="deleteTodo"/></ul></div>
</template><script>
export default {......props:["todos","checkTodo","deleteTodo"],......
}
</script>

Item.vue 接收调用,并把要删除的 id 传过去

<template><li><label><input type="checkbox" :checked="todo.done" @change="handleCheck(todo.id)"><span>{{todo.title}}</span></label><button class="btn btn-danger" @click="handleDelete(todo.id)">删除</button></li>
</template><script>
export default {......props:['todo','checkTodo','deleteTodo'],methods:{......//删除handleDelete(id){if(confirm("确认删除吗?")){this.deleteTodo(id)}}}
}
</script><style scoped>
......li:hover{background-color: #dddddd;
}li:hover button{display: block;
}
</style>

底部统计功能实现

底部要实现统计功能,必须知道 todos 的长度和选中个数,所以需要将 todos 传过来

App.vue 将 todos 传给 Footer.vue

<Footer :todos="todos"/>

Footer.vue 接收并计算,使用 reduce 函数计算选中的个数,关于reduce的用法

如果选中个数和数组个数相等,那么前边的 checkbox 将被选中。使用计算属性返回选中个数和数组个数是否相等即可

如果把项目全部删除,那么底部将整体不展示,可以给 Footer.vue 增加一个 v-show 即可

点击 checkbox 将把项目全部选中,可以给 checkbox 增加 change 事件来全选或全部选,所以需要在 App.vue 中增加方法

App.vue

<Footer :todos="todos" :checkAllTodo="checkAllTodo"/>
<script>
......export default {......methods: {......//全选 or 取消全选checkAllTodo(done){this.todos.forEach((todo)=>{todo.done = done})}}
}
</script>

Footer.vue

<template><div class="todo-footer" v-show="total"><label><input type="checkbox" :checked="isAll" @change="checkAll"></label><span><span>已完成{{ doneTotal }}</span>/全部{{ total }}</span><button class="btn btn-danger">清除已完成任务</button></div>
</template><script>
export default {name: "Footer",props: ["todos","checkAllTodo"],computed: {total(){return this.todos.length},doneTotal() {/*return this.todos.reduce((pre, current) => {return pre + (current.done ? 1 : 0)}, 0)*///精简为return this.todos.reduce((pre, todo) => pre + (todo.done ? 1 : 0), 0)},isAll(){return this.doneTotal === this.total}},methods:{checkAll(e){this.checkAllTodo(e.target.checked)}}
}
</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>

我们还可以优化一点,其中 Footer.vue 的 checkbox 可以使用 v-model 来双向绑定数据

<input type="checkbox" v-model="isAll"><script>
export default {name: "Footer",props: ["todos","checkAllTodo"],computed: {......isAll:{get(){return this.doneTotal === this.total},set(value){this.checkAllTodo(value)}}}
}
</script>

底部清除已完成任务

App.vue中增加 clearAllTodo 方法,返回未被选中的项目,然后把这个方法传给 Footer.vue

App.vue中

<Footer :todos="todos" :checkAllTodo="checkAllTodo" :clearAllTodo="clearAllTodo"/><script>
......
export default {......methods: {......//清除全部选中的任务clearAllTodo(){this.todos = this.todos.filter((todo)=>{return !todo.done})}}
}
</script>

Footer.vue接收并使用

<button class="btn btn-danger" @click="clearAll">清除已完成任务</button><script>
export default {......methods:{clearAll(){this.clearAllTodo()}}
}
</script>

总结

1、组件化编码流程:
(1).拆分静态组件:组件要按照功能点拆分,命名不要与 html 元素冲突
(2).实现动态组件:考虑好数据的存放位置,数据是一个组件在用,还是一些组件在用:
------①一个组件在用:放在组件自身即可
------②一些组件在用:放在他们共同的父组件上(状态提升)
(3).实现交互:从绑定事件开始
2、props适用于:
(1).父组件=>子组件通信
(2).子组件=>父组件通信(要求父先给子一个函数)
3、使用v-model时要切记:v-model绑定的值不能是 props 传过来的值,因为 props 是不可以修改的!!
4、props 传过来的若是对象类型的值,修改对象中的属性时 Vue 不会报错。但不推荐这样做

Vue TodoList案例相关推荐

  1. [Vue] TodoList 案例

    前言 系列文章目录: [Vue]目录 老师的课件笔记,不含视频 https://www.aliyundrive.com/s/B8sDe5u56BU 笔记在线版: https://note.youdao ...

  2. 尚硅谷todolist案例

    vue todolist案例 1 拆分组件 一共拆分为4个组件 TodoHeader TodoItem TodoList TodoFooter item是list的子组件 2 组件化编码流程 实现静态 ...

  3. Vue之Todolist案例和ES6语法

    2.7 Todolist案例 2.7.1 准备工作 <!DOCTYPE html> <html> <head><meta charset="utf- ...

  4. Vue.js-Day02-AM【Vue表单、核心指令(单选框、密码框、多行文本框、单选、多选、勾选、下拉列表)、组件汇总案例、ToDoList案例、计算属性(computed)、watch监听】

    Vue.js实训[基础理论(5天)+项目实战(5天)]博客汇总表[详细笔记] 目   录 1.Vue表单 原生JS实现异步表单提交 运行截图 代码 核心指令 单选框.密码框.多行文本框 单选 多选 勾 ...

  5. Vue版todolist案例

    Vue版todolist案例 todolist – 记录你的待办事项 <!DOCTYPE html> <html><head><meta charset=&q ...

  6. 基于vue的todolist案例

    前言:todolist案例真的算是经典了,不管你是学习原生js,还是学习jq,还是学习vue,亦或者是react,todolist是练习必不可少的练习demo了,下面我们来看看这个案例.需要完整代码的 ...

  7. Vue基础案例(水果搜索,购物车,todolist,留言板,跑马灯)

    Vue基础案例(水果搜索,购物车,todolist,留言板,跑马灯) 01.水果搜索案例 思路以及运用知识点 通过computed计算 keyword与list ,算出findlist 如果list中 ...

  8. vue实战案例:用学过的知识做一个小demo

    学过了前面11个章节的知识,可以说你已经对vue2.0的基础知识有了一定程度的掌握,虽然在真正开发过程中,一些知识的使用会稍有不同,但是别慌,我们会把那部分内容在进阶系列,比如:单文件组件,过渡效果, ...

  9. vue2.0-脚手架-todolist案例

    一 vue脚手架2.0 安装 npm install vue-cli -g 查阅一下脚手架可支持的模板 vue list可以查到 template-name 使用脚手架生成项目(以下命令得运行在项目的 ...

  10. 【Vue2.0】— TodoList案例(十七)

    [Vue2.0]- TodoList案例(十七) <template><div id="root"><div class="todo-con ...

最新文章

  1. 为物联网产业化发展提供坚实保障
  2. c++ 交换变量实践
  3. Vue 第一天学习 ---2018.06.28
  4. 编程 小数位数_使用动态编程的n位数的非递减总数
  5. LeetCode OJ Symmetric Tree 判断是否为对称树(AC代码)
  6. MediaCodec 编码H264 编码一帧后dequeueOutputBuffer为-1的问题
  7. 分享小知识:善用Group By排序
  8. html公式输入空格,mathtype怎么在公式里加空格
  9. layer常用功能-子页面关闭当前窗口-执行子页面方法-方法回调
  10. HTML怎么跟随页面缩放,如何让网页跟着 浏览器全比例缩小(示例代码)
  11. 百度地图实现鼠标绘制多边形并获取所有点坐标
  12. 我的狗子 -业余四足机器人笔记(1)-组成简介
  13. Maxcompute sql 练习题
  14. 调用WPS服务COM组件转换PDF
  15. linux 时钟同步
  16. 系统监控——监控系统选型分析及误区探讨
  17. ASPX和Razor
  18. 苹果进军RISC-V 招聘职位已发布
  19. PHPstudy用Apache的ab工具测试实例详解
  20. 破解“我的” Tesla Model 3 -> 01安全概述

热门文章

  1. 如何下载武汉市OSM数据并导出为shp格式
  2. 书单 | 这6本书,带你轻松学习自动化办公
  3. 基于optisystem的光发送机的设计和仿真
  4. 捷联惯导算法(一)程序简单实现
  5. sql语句如何获得当前日期
  6. 经典而常用的配乐和背景音乐合集(上)
  7. 经典三层模型制作学校论坛系统(BBS)
  8. android仿漫画源码、抽奖转盘、Google相册、动画源码等
  9. udp socket 接收数据
  10. 【流媒体服务】海康摄像头RTSP视频推流、转码、拉流、直播综合应用(六):【1】VLC+FFMPEG+Nginx实现RTSP到RTMP网页直播