赞
踩
<template> <div class="todo-footer" v-show="total"> <label> <!-- <input type="checkbox" :checked="isAll" @change="checkAll"/> --> <input type="checkbox" v-model="isAll" /> </label> <span> <span>已完成{{ doneTotal }}</span> / 全部{{ total }} </span> <button class="btn btn-danger" @click="clearAll">清除已完成任务</button> </div> </template> <script> export default { name: "MyFooter", props: ["todos", "checkAllTodo", "clearAllTodo"], computed: { //总数 total() { return this.todos.length; }, //已完成数 doneTotal() { /* let i=0; this.todos.forEach((todo)=>{ if(todo.done) i++; }) return i */ //此处使用reduce方法做条件统计 /* pre,上一次调用此函数返回的值(初次0) current,当前的数组项 最后一次的返回值就是reduce的返回值 const x = this.todos.reduce((pre,current)=>{ console.log('@',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 && this.total > 0 } */ isAll: { //全选框是否勾选 get() { return this.doneTotal === this.total && this.total > 0; }, //isAll被修改时set被调用 set(value) { this.checkAllTodo(value); } } }, methods: { /* // 全选 checkAll(e){ this.checkAllTodo(e.target.checked) } */ //清空所有已完成 clearAll() { 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>
<template> <div class="todo-header"> <input type="text" placeholder="请输入你的任务名称,按回车键确认" v-model="title" @keyup.enter="add"/> </div> </template> <script> import {nanoid} from 'nanoid' export default { name:'MyHeader', //接收从App传递过来的addTodo回调函数(父组件会得到从这里传入函数中的值) props:['addTodo'], data() { return { //收集用户输入的title title:'' } }, methods: { add(){ //校验数据 if(!this.title.trim()) return alert('输入不能为空') //将用户的输入包装成一个todo对象 const todoObj = {id:nanoid(),title:this.title,done:false} //通知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>
<template> <li> <label> <!-- 设置标签动态地拥有某个属性 --> <input type="checkbox" :checked="todo.done" @change="handleCheck(todo.id)" /> <!-- 如下代码也能实现功能,但是不太推荐,因为有点违反原则,因为修改了props --> <!-- <input type="checkbox" v-model="todo.done"/> --> <span>{{ todo.title }}</span> </label> <button class="btn btn-danger" @click="handleDelete(todo.id)">删除</button> </li> </template> <script> export default { name: "MyItem", //声明接收todo、checkTodo、deleteTodo props: ["todo", "checkTodo", "deleteTodo"], methods: { //勾选or取消勾选 handleCheck(id) { //通知App组件将对应的todo对象的done值取反 this.checkTodo(id); }, //删除 handleDelete(id) { if (confirm("确定删除吗?")) { //通知App组件将对应的todo对象删除 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: #ddd; } li:hover button { display: block; } </style>
<template> <ul class="todo-main"> <MyItem v-for="todoObj in todos" :key="todoObj.id" :todo="todoObj" :checkTodo="checkTodo" :deleteTodo="deleteTodo" /> </ul> </template> <script> import MyItem from './MyItem' export default { name:'MyList', components:{MyItem}, //声明接收App传递过来的数据,其中todos是自己用的,checkTodo和deleteTodo是给子组件MyItem用的 props:['todos','checkTodo','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>
<template> <div id="root"> <div class="todo-container"> <div class="todo-wrap"> <!-- 传递一个方法 --> <MyHeader :addTodo="addTodo"/> <MyList :todos="todos" :checkTodo="checkTodo" :deleteTodo="deleteTodo"/> <MyFooter :todos="todos" :checkAllTodo="checkAllTodo" :clearAllTodo="clearAllTodo"/> </div> </div> </div> </template> <script> import MyHeader from './components/MyHeader' import MyList from './components/MyList' import MyFooter from './components/MyFooter.vue' export default { name:'App', components:{MyHeader,MyList,MyFooter}, data() { return { //由于todos是MyHeader组件和MyFooter组件都在使用,所以放在App中(状态提升) todos:[ {id:'001',title:'抽烟',done:true}, {id:'002',title:'喝酒',done:false}, {id:'003',title:'开车',done:true} ] } }, methods: { //添加一个todo addTodo(todoObj){ this.todos.unshift(todoObj) console.log("收到来自子组件的数据",todoObj) }, //勾选or取消勾选一个todo checkTodo(id){ this.todos.forEach((todo)=>{ if(todo.id === id) todo.done = !todo.done }) }, //删除一个todo deleteTodo(id){ this.todos = this.todos.filter( todo => todo.id !== id ) }, //全选or取消全选 checkAllTodo(done){ this.todos.forEach((todo)=>{ todo.done = done }) }, //清除所有已经完成的todo clearAllTodo(){ this.todos = this.todos.filter((todo)=>{ return !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>
1、 组件化编码流程:
(1).拆分静态组件:组件要按照功能点拆分,命名不要与html元素冲突。
(2).实现动态组件:考虑好数据的存放位置,数据是一个组件在用,还是一些组件在用:
1).一个组件在用:放在组件自身即可。
2). 一些组件在用:放在他们共同的父组件上(状态提升)。
(3).实现交互:从绑定事件开始。
2、 props适用于:
(1).父组件 ==> 子组件 通信
(2).子组件 ==> 父组件 通信(要求父先给子一个函数)
3、 使用v-model时要切记:v-model绑定的值不能是props传过来的值,因为props是不可以修改的!
4、 props传过来的若是对象类型的值,修改对象中的属性时Vue不会报错,但不推荐这样做。
存储内容大小一般支持5MB左右(不同浏览器可能还不一样)
浏览器端通过 Window.sessionStorage 和 Window.localStorage 属性来实现本地存储机制。
相关API:
xxxxxStorage.setItem('key', 'value');
该方法接受一个键和值作为参数,会把键值对添加到存储中,如果键名存在,则更新其对应的值。
xxxxxStorage.getItem('person');
该方法接受一个键名作为参数,返回键名对应的值。
xxxxxStorage.removeItem('key');
该方法接受一个键名作为参数,并把该键名从存储中删除。
xxxxxStorage.clear()
该方法会清空存储中的所有数据。
备注:
xxxxxStorage.getItem(xxx)
如果xxx对应的value获取不到,那么getItem的返回值是null。JSON.parse(null)
的结果依然是null。修改app.vue
<template> <div id="root"> <div class="todo-container"> <div class="todo-wrap"> <MyHeader :addTodo="addTodo" /> <MyList :todos="todos" :checkTodo="checkTodo" :deleteTodo="deleteTodo" /> <MyFooter :todos="todos" :checkAllTodo="checkAllTodo" :clearAllTodo="clearAllTodo" /> </div> </div> </div> </template> <script> import MyHeader from "./components/MyHeader"; import MyList from "./components/MyList"; import MyFooter from "./components/MyFooter.vue"; export default { name: "App", components: { MyHeader, MyList, MyFooter }, data() { return { //由于todos是MyHeader组件和MyFooter组件都在使用,所以放在App中(状态提升) todos: JSON.parse(localStorage.getItem("todos")) || [] // 初始时为null,所以选择一个空数组 }; }, methods: { //添加一个todo addTodo(todoObj) { this.todos.unshift(todoObj); }, //勾选or取消勾选一个todo checkTodo(id) { this.todos.forEach((todo) => { if (todo.id === id) todo.done = !todo.done; }); }, //删除一个todo deleteTodo(id) { this.todos = this.todos.filter((todo) => todo.id !== id); }, //全选or取消全选 checkAllTodo(done) { this.todos.forEach((todo) => { todo.done = done; }); }, //清除所有已经完成的todo clearAllTodo() { this.todos = this.todos.filter((todo) => { return !todo.done; }); } }, watch: { // 没有深度监视 // todos(value) { // localStorage.setItem("todos", JSON.stringify(value)); // }, // 开启了深度监视 todos: { deep: true, handler(value) { localStorage.setItem("todos", JSON.stringify(value)); } } } }; </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>
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。