VueCLI核心知识综合案例TodoList

2024-02-15 06:04

本文主要是介绍VueCLI核心知识综合案例TodoList,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

1 拿到一个功能模块首先需要拆分组件:

2 使用组件实现静态页面的效果

3 分析数据保存在哪个组件

4 实现添加数据

5 实现复选框勾选

6 实现数据的删除

7 实现底部组件中数据的统计

8 实现勾选全部的小复选框来实现大复选框的勾选

9 实现勾选大复选框来实现所有的小复选框都被勾选

10 清空所有数据

11 实现案例中的数据存入本地存储

12 案例中使用自定义事件完成组件间的数据通信

13 案例中实现数据的编辑

14 实现数据进出的动画效果


【分析】组件化编码的流程

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

2.展示动态数据:

        2.1 数据的类型、名称是什么?

        2.2 数据保存在哪个组件?

3.交互---从绑定事件监听开始


1 拿到一个功能模块首先需要拆分组件:


2 使用组件实现静态页面的效果

【main.js】

import Vue from 'vue'
import App from './App.vue'Vue.config.productionTip = falsenew Vue({el: '#app',render: h => h(App)
})

【MyHeader】

<template><div class="todo-header"><input type="text"/></div>
</template><script>export default {name: 'MyHeader',}
</script><style scoped>/*header*/.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.6);}
</style>

【Item】

<template> <li><label><input type="checkbox"/><span v-for="todo in todos" :key="todo.id">{{todo.title}}</span></label><button class="btn btn-danger">删除</button></li>
</template><script>export default {name: 'Item',data() {return {todos: [{id: '001', title: '吃饭', done: true},{id: '002', title: '学习', done: false},{id: '003', title: '追剧', done: true},]}},}
</script><style scoped>/*item*/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;}li:hover {background-color: rgb(196, 195, 195);}li:hover button{display: block;}
</style>

【List】

<template><ul class="todo-main"><Item></Item><Item></Item></ul>
</template><script>import Item from './Item.vue'export default {name: 'List',components:{Item},}
</script><style scoped>/*main*/.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>

【MyFooter】

<template><div class="todo-footer"><label><input type="checkbox"/></label><span><span>已完成 0</span> / 3</span><button class="btn btn-danger">清除已完成任务</button></div>
</template><script>export default {name: 'MyFooter',}
</script><style scoped>/*footer*/.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>

【App】 

<template><div id="root"><div class="todo-container"><div class="todo-wrap"><MyHeader></MyHeader><List></List><MyFooter></MyFooter></div></div></div>
</template><script>import MyHeader from './components/MyHeader.vue'import List from './components/List.vue'import MyFooter from './components/MyFooter.vue'export default {name:'App',components:{MyHeader,List,MyFooter} }
</script><style>/*base*/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-danger:hover {color: #fff;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>

通过以上代码就可以实现静态页面的效果了!!!

3 分析数据保存在哪个组件

在上述代码中数据是保存在Item组件中的,但是如果想要在后续实现一系列交互效果:在MyHeader组件中需要添加数据,而MyHeader组件和Item组件没有直接的关系, 就当前学习阶段的知识而言,并不能实现这两个组件之间的通信(后续会有解决方案),同理MyFooter也一样。


【分析】因为App组件是所有组件的父组件,所以数据放在App组件中,再使用props配置,所有的子组件就都可以访问到。

【App】(同时需要将数据传递到Item组件中,在当前阶段只能通过props配置一层一层往下传,所以是 App-->List,List-->Item

1. 实现 App-->List 传递todos数据

<template><div id="root"><div class="todo-container"><div class="todo-wrap"><MyHeader></MyHeader><List :todos="todos"></List><MyFooter></MyFooter></div></div></div>
</template><script>import MyHeader from './components/MyHeader.vue'import List from './components/List.vue'import MyFooter from './components/MyFooter.vue'export default {name:'App',components:{MyHeader,List,MyFooter},data() {return {todos: [{id: '001', title: '吃饭', done: true},{id: '002', title: '学习', done: false},{id: '003', title: '追剧', done: true},]}},}
</script><style>/*base*/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-danger:hover {color: #fff;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>

2. List接受todos数据

<template><ul class="todo-main"><Item v-for="todo in todos" :key="todo.id"></Item></ul>
</template><script>import Item from './Item.vue'export default {name: 'List',components:{Item},props: ['todos']}
</script><style scoped>/*main*/.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了,但是此时Item里面是没有内容的,所以需要 List-->Item 再次传递每条数据


3. 实现 List-->Item 传递todo数据

<template><ul class="todo-main"><Item v-for="todo in todos" :key="todo.id" :todo="todo"></Item></ul>
</template><script>import Item from './Item.vue'export default {name: 'List',components:{Item},props: ['todos']}
</script><style scoped>/*main*/.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>

4. Item接受todo数据

<template> <li><label><input type="checkbox"/><span>{{todo.title}}</span></label><button class="btn btn-danger">删除</button></li>
</template><script>export default {name: 'Item',// 声明接收todo对象props:['todo'],}
</script><style scoped>/*item*/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;}li:hover {background-color: rgb(196, 195, 195);}li:hover button{display: block;}
</style>

4 实现添加数据

【App】定义接收数据的回调函数

<template><div id="root"><div class="todo-container"><div class="todo-wrap"><MyHeader :addTodo="addTodo"></MyHeader><List :todos="todos"></List><MyFooter></MyFooter></div></div></div>
</template><script>import MyHeader from './components/MyHeader.vue'import List from './components/List.vue'import MyFooter from './components/MyFooter.vue'export default {name:'App',components:{MyHeader,List,MyFooter},data() {return {todos: [{id: '001', title: '吃饭', done: true},{id: '002', title: '学习', done: false},{id: '003', title: '追剧', done: true},]}},methods: {// 添加一个todoaddTodo(todoObj) {this.todos.unshift(todoObj)}}}
</script><style>/*base*/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-danger:hover {color: #fff;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>

【MyHeader】实现添加数据的方法

<template><div class="todo-header"><!-- 绑定键盘回车事件 --><input type="text" placeholder="请输入你的任务名称,按回车键确认" @keyup.enter="add" v-model="title"/></div>
</template><script>import {nanoid} from 'nanoid'  // 生成idexport default {name: 'MyHeader',data() {return {title: ''}},props: ['addTodo'],  // 接收父组件传过来的addTodo函数methods: {add(e) {// 校验数据if (!this.title.trim()) return alert('输入不能为空')// 将用户的输入包装成为一个todo对象const todoObj = {id: nanoid(),/* title: e.target.value, */title: this.title,done: false}console.log(todoObj)// console.log(e.target.value)// console.log(this.title)// 通知App组件去添加一个todo对象this.addTodo(todoObj)// 清空输入this.title = ''}}}
</script><style scoped>/*header*/.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.6);}
</style>

5 实现复选框勾选

【App】也是要逐层传递

<template><div id="root"><div class="todo-container"><div class="todo-wrap"><MyHeader :addTodo="addTodo"></MyHeader><List :todos="todos" :changeTodo="changeTodo"></List><MyFooter></MyFooter></div></div></div>
</template><script>import MyHeader from './components/MyHeader.vue'import List from './components/List.vue'import MyFooter from './components/MyFooter.vue'export default {name:'App',components:{MyHeader,List,MyFooter},data() {return {todos: [{id: '001', title: '吃饭', done: true},{id: '002', title: '学习', done: false},{id: '003', title: '追剧', done: true},]}},methods: {// 添加一个todoaddTodo(todoObj) {this.todos.unshift(todoObj)},// 勾选或者取消勾选一个todochangeTodo(id) {this.todos.forEach((todo) => {if (todo.id === id) todo.done = !todo.done})},}}
</script><style>/*base*/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-danger:hover {color: #fff;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>

【List】

<template><ul class="todo-main"><Item v-for="todo in todos" :key="todo.id" :todo="todo":changeTodo="changeTodo"></Item></ul>
</template><script>import Item from './Item.vue'export default {name: 'List',components:{Item},props: ['todos', 'changeTodo']}
</script><style scoped>/*main*/.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】

<template> <li><label><input type="checkbox" :checked="todo.done" @change="handleCheck(todo.id)"/><span>{{todo.title}}</span></label><button class="btn btn-danger">删除</button></li>
</template><script>export default {name: 'Item',// 声明接收todo对象props:['todo', 'changeTodo'],methods: {// 勾选 or 取消勾选handleCheck(id) {// 通知 App组件将对应的todo对象的状态改变this.changeTodo(id)}}}
</script><style scoped>/*item*/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;}li:hover {background-color: rgb(196, 195, 195);}li:hover button{display: block;}
</style>

6 实现数据的删除

【App】

<template><div id="root"><div class="todo-container"><div class="todo-wrap"><MyHeader :addTodo="addTodo"></MyHeader><List :todos="todos" :changeTodo="changeTodo" :deleteTodo="deleteTodo"></List><MyFooter></MyFooter></div></div></div>
</template><script>import MyHeader from './components/MyHeader.vue'import List from './components/List.vue'import MyFooter from './components/MyFooter.vue'export default {name:'App',components:{MyHeader,List,MyFooter},data() {return {todos: [{id: '001', title: '吃饭', done: true},{id: '002', title: '学习', done: false},{id: '003', title: '追剧', done: true},]}},methods: {// 添加一个todoaddTodo(todoObj) {this.todos.unshift(todoObj)},// 勾选或者取消勾选一个todochangeTodo(id) {this.todos.forEach((todo) => {if (todo.id === id) todo.done = !todo.done})},// 删除一个tododeleteTodo(id) {this.todos = this.todos.filter((todo) => todo.id !== id)},}}
</script><style>/*base*/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-danger:hover {color: #fff;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>

【List】

<template><ul class="todo-main"><Item v-for="todo in todos" :key="todo.id" :todo="todo":changeTodo="changeTodo":deleteTodo="deleteTodo"></Item></ul>
</template><script>import Item from './Item.vue'export default {name: 'List',components:{Item},props: ['todos', 'changeTodo', 'deleteTodo']}
</script><style scoped>/*main*/.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】

<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 {name: 'Item',// 声明接收todo对象props:['todo', 'changeTodo', 'deleteTodo'],methods: {// 勾选 or 取消勾选handleCheck(id) {// 通知 App组件将对应的todo对象的状态改变this.changeTodo(id)},// 删除操作handleDelete(id) {// console.log(id)if (confirm("确定删除吗?")) {// 通知App删除this.deleteTodo(id)}}}}
</script><style scoped>/*item*/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;}li:hover {background-color: rgb(196, 195, 195);}li:hover button{display: block;}
</style>

7 实现底部组件中数据的统计

【分析】如果想要统计数据的数量,就需要将数据传递到MyFooter组件中

【App】

<template><div id="root"><div class="todo-container"><div class="todo-wrap"><MyHeader :addTodo="addTodo"></MyHeader><List :todos="todos" :changeTodo="changeTodo" :deleteTodo="deleteTodo"></List><MyFooter:todos="todos"></MyFooter></div></div></div>
</template><script>import MyHeader from './components/MyHeader.vue'import List from './components/List.vue'import MyFooter from './components/MyFooter.vue'export default {name:'App',components:{MyHeader,List,MyFooter},data() {return {todos: [{id: '001', title: '吃饭', done: true},{id: '002', title: '学习', done: false},{id: '003', title: '追剧', done: true},]}},methods: {// 添加一个todoaddTodo(todoObj) {this.todos.unshift(todoObj)},// 勾选或者取消勾选一个todochangeTodo(id) {this.todos.forEach((todo) => {if (todo.id === id) todo.done = !todo.done})},// 删除一个tododeleteTodo(id) {this.todos = this.todos.filter((todo) => todo.id !== id)},}}
</script><style>/*base*/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-danger:hover {color: #fff;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>

【MyFooter】

在这个组件中可以使用计算属性实现数据的总长度和被勾选的数据的计算

<template><div class="todo-footer" v-if="todosLength"><label><input type="checkbox"/></label><span><span>已完成 {{doneTotal}}</span> / {{todosLength}}</span><button class="btn btn-danger">清除已完成任务</button></div>
</template><script>
export default {name: 'MyFooter',props: ['todos'],computed: {todosLength() {return this.todos.length},doneTotal() {return this.todos.filter(todo => todo.done).length// 也可以使用下面求和来实现// return this.todos.reduce((pre, todo) => pre + (todo.done ? 1 : 0), 0)}}
}
</script><style scoped>/*footer*/.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>

8 实现勾选全部的小复选框来实现大复选框的勾选

:checked="isAll"

isAll也是通过计算属性计算得来的

【MyFooter】 

<template><div class="todo-footer" v-if="todosLength"><label><input type="checkbox" :checked="isAll"/></label><span><span>已完成 {{doneTotal}}</span> / {{todosLength}}</span><button class="btn btn-danger">清除已完成任务</button></div>
</template><script>
export default {name: 'MyFooter',props: ['todos'],computed: {todosLength() {return this.todos.length},doneTotal() {return this.todos.filter(todo => todo.done).length// 也可以使用下面求和来实现// return this.todos.reduce((pre, todo) => pre + (todo.done ? 1 : 0), 0)},isAll() {return this.doneTotal === this.todosLength && this.todosLength > 0}, }
}
</script><style scoped>/*footer*/.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>

9 实现勾选大复选框来实现所有的小复选框都被勾选

【App】

<template><div id="root"><div class="todo-container"><div class="todo-wrap"><MyHeader :addTodo="addTodo"></MyHeader><List :todos="todos" :changeTodo="changeTodo" :deleteTodo="deleteTodo"></List><MyFooter:todos="todos":checkAllTodo="checkAllTodo"></MyFooter></div></div></div>
</template><script>import MyHeader from './components/MyHeader.vue'import List from './components/List.vue'import MyFooter from './components/MyFooter.vue'export default {name:'App',components:{MyHeader,List,MyFooter},data() {return {todos: [{id: '001', title: '吃饭', done: true},{id: '002', title: '学习', done: false},{id: '003', title: '追剧', done: true},]}},methods: {// 添加一个todoaddTodo(todoObj) {this.todos.unshift(todoObj)},// 勾选或者取消勾选一个todochangeTodo(id) {this.todos.forEach((todo) => {if (todo.id === id) todo.done = !todo.done})},// 删除一个tododeleteTodo(id) {this.todos = this.todos.filter((todo) => todo.id !== id)},// 全选or取消全选checkAllTodo(done) {this.todos.forEach((todo) => {todo.done = done})},}}
</script><style>/*base*/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-danger:hover {color: #fff;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>

【MyFooter】

<template><div class="todo-footer" v-if="todosLength"><label><input type="checkbox" :checked="isAll" @change="checkAll"/></label><span><span>已完成 {{doneTotal}}</span> / {{todosLength}}</span><button class="btn btn-danger">清除已完成任务</button></div>
</template><script>
export default {name: 'MyFooter',props: ['todos', 'checkAllTodo'],computed: {todosLength() {return this.todos.length},doneTotal() {return this.todos.filter(todo => todo.done).length// 也可以使用下面求和来实现// return this.todos.reduce((pre, todo) => pre + (todo.done ? 1 : 0), 0)},isAll() {return this.doneTotal === this.todosLength && this.todosLength > 0}, },methods: {checkAll(e) {this.checkAllTodo(e.target.checked)}}
}
</script><style scoped>/*footer*/.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>

10 清空所有数据

【App】

<template><div id="root"><div class="todo-container"><div class="todo-wrap"><MyHeader :addTodo="addTodo"></MyHeader><List :todos="todos" :changeTodo="changeTodo" :deleteTodo="deleteTodo"></List><MyFooter:todos="todos":checkAllTodo="checkAllTodo":clearAllTodo="clearAllTodo"></MyFooter></div></div></div>
</template><script>import MyHeader from './components/MyHeader.vue'import List from './components/List.vue'import MyFooter from './components/MyFooter.vue'export default {name:'App',components:{MyHeader,List,MyFooter},data() {return {todos: [{id: '001', title: '吃饭', done: true},{id: '002', title: '学习', done: false},{id: '003', title: '追剧', done: true},]}},methods: {// 添加一个todoaddTodo(todoObj) {this.todos.unshift(todoObj)},// 勾选或者取消勾选一个todochangeTodo(id) {this.todos.forEach((todo) => {if (todo.id === id) todo.done = !todo.done})},// 删除一个tododeleteTodo(id) {this.todos = this.todos.filter((todo) => todo.id !== id)},// 全选or取消全选checkAllTodo(done) {this.todos.forEach((todo) => {todo.done = done})},// 清空所有已经完成的todoclearAllTodo() {this.todos = this.todos.filter((todo) => !todo.done)}}}
</script><style>/*base*/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-danger:hover {color: #fff;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>

【MyFooter】

<template><div class="todo-footer" v-if="todosLength"><label><input type="checkbox" :checked="isAll" @change="checkAll"/></label><span><span>已完成 {{doneTotal}}</span> / {{todosLength}}</span><button class="btn btn-danger"  @click="clearTodo">清除已完成任务</button></div>
</template><script>
export default {name: 'MyFooter',props: ['todos', 'checkAllTodo', 'clearAllTodo'],computed: {todosLength() {return this.todos.length},doneTotal() {return this.todos.filter(todo => todo.done).length// 也可以使用下面求和来实现// return this.todos.reduce((pre, todo) => pre + (todo.done ? 1 : 0), 0)},isAll() {return this.doneTotal === this.todosLength && this.todosLength > 0}, },methods: {checkAll(e) {this.checkAllTodo(e.target.checked)},clearTodo() {this.clearAllTodo()}}
}
</script><style scoped>/*footer*/.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>

11 实现案例中的数据存入本地存储

【分析】首先我们要知道什么时候需要将数据存入本地存储?所以这就用到了watch监听,当todos的值发生变化时,将新的值存入本地存储。

又因为当我们勾选复选框时,我们发现本地存储中的 done 值并没有发生变化?这主要是因为监听默认只会监听第一层,如果想要监听对象中某个数据发生变化时,就需要深度监视了。

【App】

这里使用 || 运算可以防止一开始本地存储中没有数据而报错

12 案例中使用自定义事件完成组件间的数据通信

这边以添加数据为例

【App】

给发送数据的组件绑定自定义事件

<MyHeader @addTodo="addTodo"></MyHeader>...methods: {// 添加一个todoaddTodo(todoObj) {this.todos.unshift(todoObj)},
}

【MyHeader】

13 案例中实现数据的编辑

需求分析:当点击编辑按钮时,变成input表单修改数据,此时编辑按钮隐藏,当失去焦点时,编辑完成,显示编辑后的数据,同时编辑按钮显示。

 这边使用全局事件总线来实现通信

【App】

        methods: {...// 更改updateTodo(id,title) {this.todos.forEach((todo) => {if (todo.id === id) todo.title = title})},...},mounted() {this.$bus.$on('updateTodo', this.updateTodo)},beforeDestroy() {this.$bus.$off('updateTodo')}

【Item】



因为如果想要失去焦点时实现数据的修改,那么你必须提前获取焦点,但是由于Vue的执行机制,当Vue底层监视到数据发生改变时,它并不会立即去重新渲染模板,而是继续执行后面的代码,所以如果不加以处理的话,直接获取焦点,肯定会报错,因为页面中的元素还没有加载解析出,找不到获取焦点的input元素,所以可以通过以下的代码实现

this.$nextTick(function() {  // 告诉Vue,DOM渲染完毕后,再执行focus()方法this.$refs.inputTiltle.focus()
})

14 实现数据进出的动画效果

【Item】

使用<transtion></transtion>标签包裹


这篇关于VueCLI核心知识综合案例TodoList的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

使用Navicat工具比对两个数据库所有表结构的差异案例详解

《使用Navicat工具比对两个数据库所有表结构的差异案例详解》:本文主要介绍如何使用Navicat工具对比两个数据库test_old和test_new,并生成相应的DDLSQL语句,以便将te... 目录概要案例一、如图两个数据库test_old和test_new进行比较:二、开始比较总结概要公司存在多

SpringBoot实现动态插拔的AOP的完整案例

《SpringBoot实现动态插拔的AOP的完整案例》在现代软件开发中,面向切面编程(AOP)是一种非常重要的技术,能够有效实现日志记录、安全控制、性能监控等横切关注点的分离,在传统的AOP实现中,切... 目录引言一、AOP 概述1.1 什么是 AOP1.2 AOP 的典型应用场景1.3 为什么需要动态插

Golang操作DuckDB实战案例分享

《Golang操作DuckDB实战案例分享》DuckDB是一个嵌入式SQL数据库引擎,它与众所周知的SQLite非常相似,但它是为olap风格的工作负载设计的,DuckDB支持各种数据类型和SQL特性... 目录DuckDB的主要优点环境准备初始化表和数据查询单行或多行错误处理和事务完整代码最后总结Duck

MySQL不使用子查询的原因及优化案例

《MySQL不使用子查询的原因及优化案例》对于mysql,不推荐使用子查询,效率太差,执行子查询时,MYSQL需要创建临时表,查询完毕后再删除这些临时表,所以,子查询的速度会受到一定的影响,本文给大家... 目录不推荐使用子查询和JOIN的原因解决方案优化案例案例1:查询所有有库存的商品信息案例2:使用EX

Java架构师知识体认识

源码分析 常用设计模式 Proxy代理模式Factory工厂模式Singleton单例模式Delegate委派模式Strategy策略模式Prototype原型模式Template模板模式 Spring5 beans 接口实例化代理Bean操作 Context Ioc容器设计原理及高级特性Aop设计原理Factorybean与Beanfactory Transaction 声明式事物

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

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

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

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

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

Andrej Karpathy最新采访:认知核心模型10亿参数就够了,AI会打破教育不公的僵局

夕小瑶科技说 原创  作者 | 海野 AI圈子的红人,AI大神Andrej Karpathy,曾是OpenAI联合创始人之一,特斯拉AI总监。上一次的动态是官宣创办一家名为 Eureka Labs 的人工智能+教育公司 ,宣布将长期致力于AI原生教育。 近日,Andrej Karpathy接受了No Priors(投资博客)的采访,与硅谷知名投资人 Sara Guo 和 Elad G

综合安防管理平台LntonAIServer视频监控汇聚抖动检测算法优势

LntonAIServer视频质量诊断功能中的抖动检测是一个专门针对视频稳定性进行分析的功能。抖动通常是指视频帧之间的不必要运动,这种运动可能是由于摄像机的移动、传输中的错误或编解码问题导致的。抖动检测对于确保视频内容的平滑性和观看体验至关重要。 优势 1. 提高图像质量 - 清晰度提升:减少抖动,提高图像的清晰度和细节表现力,使得监控画面更加真实可信。 - 细节增强:在低光条件下,抖