Vue基础11
- TodoList案例
- 组件拆分
- 静态组件
- 代码目录结构
- App.vue
- Header.vue
- List.vue
- Item.vue
- Footer.vue
- 初始化列表
- List.vue
- Item.vue
- 添加功能
- 使用nanoid作为每项的id值
- 安装nanoid
- 使用nanoid
- 兄弟组件之间的传值
- App.vue
- Header.vue
- List.vue
- 勾选
- App.vue
- List.vue
- Item.vue
- 使用v-model也能实现修改功能,不过不推荐使用
- App.vue
- List.vue
- Item.vue
TodoList案例
组件拆分
样式结构:
静态组件
代码目录结构
App.vue
<template><div class="bg"><div class="todoList"><h2 class="title">待办事项</h2><Header /><div class="listItem"><List /><Footer /></div></div></div>
</template><script>import Header from "@/components/Header";
import List from "@/components/List";
import Footer from "@/components/Footer";
export default {name: "App",components:{Footer, Header, List},data(){return{appText:this.text}}
}
</script><style lang="less">
*{padding: 0;margin: 0;
}
.bg{background-color: #333;height: 937px;padding-top: 100px;box-sizing: border-box;.todoList{background-color: #fff;width: 50%;height: 90%;margin: 0 auto;//box-shadow: 5px 5px 10px 3px rgba(147, 221, 255, 0.5),-5px -5px 10px 3px rgba(147, 221, 255, 0.5); 蓝色阴影box-shadow: 5px 5px 10px 3px rgba(0, 0, 0, 0.5),-5px -5px 10px 3px rgba(0, 0, 0, 0.5);padding-top: 20px;box-sizing: border-box;.title{text-align: center;font-size: 30px;font-weight: 300;color: #00a4ff;}.listItem{width: 90%;//height: 200px;margin: auto;/*background-color: pink;*/list-style: none;border-radius: 0 0 5px 5px;box-shadow: 1px 1px 5px 1px rgba(0,0,0,0.1),-1px -1px 5px 1px rgba(0,0,0,0.1);padding: 20px 0;box-sizing: border-box;}}
}
</style>
Header.vue
<template><div><input type="text" class="content" @keyup.enter="enSure" placeholder="请输入你的任务名称,按回车键确认"></div>
</template><script>
export default {name: "Header",data(){return{InputText:''}},methods:{enSure(element){this.InputText=element.target.value;console.log(element.target.value)}}
}
</script><style scoped lang="less">
div{width: 90%;height: 8%;background-color: #ffffff;box-shadow: 1px 1px 5px 1px rgba(0,0,0,0.1),-1px -1px 5px 1px rgba(0,0,0,0.1);margin: 10px auto 2px auto;display: flex;.content{width: 95%;height: 80%;font-size: 25px;outline: none;display: block;margin: auto;justify-content: center;align-self: center;border: none;}
}
</style>
List.vue
<template><div><ul><div class="con"><Item :messages='["打代码","睡觉","吃饭"]'/></div></ul></div>
</template><script>
import Item from "@/components/Item";
export default {name:'List',components:{Item},
}
</script><style scoped lang="less">
ul{.con{//width: 95%;//margin: auto;border-bottom: 1px solid rgba(87, 87, 87, 0.3);border-left: 1px solid rgba(87, 87, 87, 0.3);border-right: 1px solid rgba(87, 87, 87, 0.3);margin: 0px 8px;//background-color: pink;}
}
</style>
Item.vue
<template><div><li v-for="(msg,index) in messages" :key="index"><input type="checkbox" name="matter" id=""> {{msg}}<button class="delete">删除</button></li></div>
</template><script>export default {name: "Item",props:{messages:{type:Array,default:['123233','5125656']}}}
</script><style scoped lang="less">
li{//height: 35%;//width: 96%;display: block;//background-color: pink;margin: auto;padding: 12px;border-top: 1px solid rgba(87, 87, 87, 0.3);//border-left: 1px solid rgba(87, 87, 87, 0.3);//border-right: 1px solid rgba(87, 87, 87, 0.3);//box-sizing: border-box;border-collapse: collapse;button{background-color: #d9534f;float: right;padding: 3px 10px;color: white;border: 1px solid #d43f3a;border-radius: 5px;cursor: pointer;&:hover{background-color: #c9302c;border: 1px solid #ac2925;}}&:hover{background-color: rgba(0,0,0,0.1);}
}
</style>
Footer.vue
<template>
<div><input type="checkbox" name="matter" id=""> 已完成 <span>0</span> / 全部<span>3</span><button>清除已完成任务</button>
</div>
</template><script>export default {name: "Footer"}
</script><style scoped lang="less">
div{width: 95%;margin: auto;margin-top: 10px;//border: 1px solid rgba(87, 87, 87, 0.3);button{background-color: #d9534f;float: right;padding: 3px 10px;color: white;border: 1px solid #d43f3a;border-radius: 5px;cursor: pointer;&:hover{background-color: #c9302c;border: 1px solid #ac2925;}
}
}
</style>
初始化列表
List.vue
<template><div><ul><div class="con"><Item v-for="todoObj in todos" :key="todoObj.id" :todo="todoObj"/></div></ul></div>
</template><script>
import Item from "@/components/Item";
export default {name:'List',components:{Item},data(){return{todos:[{id:'001',title:'上班',done:false},{id:'002',title:'吃饭',done:false},{id:'003',title:'看电影',done:false},]}}
}
</script><style scoped lang="less">
ul{.con{//width: 95%;//margin: auto;border-bottom: 1px solid rgba(87, 87, 87, 0.3);border-left: 1px solid rgba(87, 87, 87, 0.3);border-right: 1px solid rgba(87, 87, 87, 0.3);margin: 0px 8px;//background-color: pink;}
}
</style>
Item.vue
<template><div><li><input type="checkbox" name="matter" id="" :checked="todo.done"> {{todo.title}}<button class="delete">删除</button></li></div>
</template><script>export default {name: "Item",props:['todo']}
</script><style scoped lang="less">
li{//height: 35%;//width: 96%;display: block;//background-color: pink;margin: auto;padding: 12px;border-top: 1px solid rgba(87, 87, 87, 0.3);//border-left: 1px solid rgba(87, 87, 87, 0.3);//border-right: 1px solid rgba(87, 87, 87, 0.3);//box-sizing: border-box;border-collapse: collapse;button{background-color: #d9534f;float: right;padding: 3px 10px;color: white;border: 1px solid #d43f3a;border-radius: 5px;cursor: pointer;&:hover{background-color: #c9302c;border: 1px solid #ac2925;}}&:hover{background-color: rgba(0,0,0,0.1);}
}
</style>
添加功能
使用nanoid作为每项的id值
安装nanoid
npm i nanoid
使用nanoid
兄弟组件之间的传值
App.vue
<template><div class="bg"><div class="todoList"><h2 class="title">待办事项</h2><Header :addTodo="addTodo"/><div class="listItem"><List :todos="todos"/><Footer /></div></div></div>
</template><script>import Header from "@/components/Header";
import List from "@/components/List";
import Footer from "@/components/Footer";
export default {name: "App",components:{Footer, Header, List},data(){return{todos:[{id:'001',title:'吃饭',done:false},{id:'002',title:'唱歌',done:false},{id:'003',title:'看电影',done:false},]}},methods:{addTodo(addObj){this.todos.unshift(addObj)}}
}
</script><style lang="less">
*{padding: 0;margin: 0;
}
.bg{background-color: #333;height: 937px;padding-top: 100px;box-sizing: border-box;.todoList{background-color: #fff;width: 50%;height: 90%;margin: 0 auto;//box-shadow: 5px 5px 10px 3px rgba(147, 221, 255, 0.5),-5px -5px 10px 3px rgba(147, 221, 255, 0.5); 蓝色阴影box-shadow: 5px 5px 10px 3px rgba(0, 0, 0, 0.5),-5px -5px 10px 3px rgba(0, 0, 0, 0.5);padding-top: 20px;box-sizing: border-box;.title{text-align: center;font-size: 30px;font-weight: 300;color: #00a4ff;}.listItem{width: 90%;//height: 200px;margin: auto;/*background-color: pink;*/list-style: none;border-radius: 0 0 5px 5px;box-shadow: 1px 1px 5px 1px rgba(0,0,0,0.1),-1px -1px 5px 1px rgba(0,0,0,0.1);padding: 20px 0;box-sizing: border-box;}}
}
</style>
Header.vue
<template><div><input type="text" class="content" v-model="title" @keyup.enter="add" placeholder="请输入你的任务名称,按回车键确认"></div>
</template><script>
import {nanoid} from 'nanoid'
export default {name: "Header",data(){return{title:''}},props:['addTodo'],methods:{add(){//校验数据if(!this.title.trim()) return alert("输入的值不得为空");//将用户输入包装成一个todo对象const addObj={id:nanoid(),title:this.title,done:false};//通知App组件去添加一个todo对象this.addTodo(addObj)//清空输入this.title=''}}
}
</script><style scoped lang="less">
div{width: 90%;height: 8%;background-color: #ffffff;box-shadow: 1px 1px 5px 1px rgba(0,0,0,0.1),-1px -1px 5px 1px rgba(0,0,0,0.1);margin: 10px auto 2px auto;display: flex;.content{width: 95%;height: 80%;font-size: 25px;outline: none;display: block;margin: auto;justify-content: center;align-self: center;border: none;}
}
</style>
List.vue
<template><div><ul><div class="con"><Item v-for="todoObj in todos" :key="todoObj.id" :todo="todoObj"/></div></ul></div>
</template><script>
import Item from "@/components/Item";
export default {name:'List',components:{Item},props:['todos']
}
</script><style scoped lang="less">
ul{.con{//width: 95%;//margin: auto;border-bottom: 1px solid rgba(87, 87, 87, 0.3);border-left: 1px solid rgba(87, 87, 87, 0.3);border-right: 1px solid rgba(87, 87, 87, 0.3);margin: 0px 8px;//background-color: pink;}
}
</style>
勾选
App.vue
<template><div class="bg"><div class="todoList"><h2 class="title">待办事项</h2><Header :addTodo="addTodo"/><div class="listItem"><List :todos="todos" :checkTodo="checkTodo"/><Footer /></div></div></div>
</template><script>import Header from "@/components/Header";
import List from "@/components/List";
import Footer from "@/components/Footer";
export default {name: "App",components:{Footer, Header, List},data(){return{todos:[{id:'001',title:'吃饭',done:false},{id:'002',title:'唱歌',done:false},{id:'003',title:'看电影',done:false},]}},methods:{//添加一个todoaddTodo(addObj){this.todos.unshift(addObj)},checkTodo(id){this.todos.forEach((todo)=>{if(todo.id==id) {todo.done=!todo.done}})}}
}
</script><style lang="less">
*{padding: 0;margin: 0;
}
.bg{background-color: #333;height: 937px;padding-top: 100px;box-sizing: border-box;.todoList{background-color: #fff;width: 50%;height: 90%;margin: 0 auto;//box-shadow: 5px 5px 10px 3px rgba(147, 221, 255, 0.5),-5px -5px 10px 3px rgba(147, 221, 255, 0.5); 蓝色阴影box-shadow: 5px 5px 10px 3px rgba(0, 0, 0, 0.5),-5px -5px 10px 3px rgba(0, 0, 0, 0.5);padding-top: 20px;box-sizing: border-box;.title{text-align: center;font-size: 30px;font-weight: 300;color: #00a4ff;}.listItem{width: 90%;//height: 200px;margin: auto;/*background-color: pink;*/list-style: none;border-radius: 0 0 5px 5px;box-shadow: 1px 1px 5px 1px rgba(0,0,0,0.1),-1px -1px 5px 1px rgba(0,0,0,0.1);padding: 20px 0;box-sizing: border-box;}}
}
</style>
List.vue
<template><div><ul><div class="con"><Item v-for="todoObj in todos" :key="todoObj.id" :todo="todoObj" :checkTodo="checkTodo"/></div></ul></div>
</template><script>
import Item from "@/components/Item";
export default {name:'List',components:{Item},props:['todos','checkTodo']
}
</script><style scoped lang="less">
ul{.con{//width: 95%;//margin: auto;border-bottom: 1px solid rgba(87, 87, 87, 0.3);border-left: 1px solid rgba(87, 87, 87, 0.3);border-right: 1px solid rgba(87, 87, 87, 0.3);margin: 0px 8px;//background-color: pink;}
}
</style>
Item.vue
<template><div><li><input type="checkbox" name="matter" id="" :checked="todo.done" @change="handleCheck(todo.id)"> {{todo.title}}<button class="delete">删除</button></li></div>
</template><script>export default {name: "Item",//声明接收todo对象props:['todo','checkTodo'],methods:{handleCheck(id){//通知App组件将对应的todo对象的done值取反this.checkTodo(id)}}}
</script><style scoped lang="less">
li{//height: 35%;//width: 96%;display: block;//background-color: pink;margin: auto;padding: 12px;border-top: 1px solid rgba(87, 87, 87, 0.3);//border-left: 1px solid rgba(87, 87, 87, 0.3);//border-right: 1px solid rgba(87, 87, 87, 0.3);//box-sizing: border-box;border-collapse: collapse;button{background-color: #d9534f;float: right;padding: 3px 10px;color: white;border: 1px solid #d43f3a;border-radius: 5px;cursor: pointer;&:hover{background-color: #c9302c;border: 1px solid #ac2925;}}&:hover{background-color: rgba(0,0,0,0.1);}
}
</style>
使用v-model也能实现修改功能,不过不推荐使用
在Item.vue中的checkbox使用v-model双向绑定,虽然也能实现功能,但是不推荐,因为有点违反原则,因为修改了props值,Vue的检测是浅度检测,props中是todo对象时,使用todo.done改值后,地址值并没有修改,则Vue检测不到,所以未报错,但是一旦使用变量存储就会报错
App.vue
<template><div class="bg"><div class="todoList"><h2 class="title">待办事项</h2><Header :addTodo="addTodo"/><div class="listItem"><List :todos="todos"/><Footer /></div></div></div>
</template><script>import Header from "@/components/Header";
import List from "@/components/List";
import Footer from "@/components/Footer";
export default {name: "App",components:{Footer, Header, List},data(){return{todos:[{id:'001',title:'吃饭',done:false},{id:'002',title:'唱歌',done:false},{id:'003',title:'看电影',done:false},]}},methods:{//添加一个todoaddTodo(addObj){this.todos.unshift(addObj)}}
}
</script><style lang="less">
*{padding: 0;margin: 0;
}
.bg{background-color: #333;height: 937px;padding-top: 100px;box-sizing: border-box;.todoList{background-color: #fff;width: 50%;height: 90%;margin: 0 auto;//box-shadow: 5px 5px 10px 3px rgba(147, 221, 255, 0.5),-5px -5px 10px 3px rgba(147, 221, 255, 0.5); 蓝色阴影box-shadow: 5px 5px 10px 3px rgba(0, 0, 0, 0.5),-5px -5px 10px 3px rgba(0, 0, 0, 0.5);padding-top: 20px;box-sizing: border-box;.title{text-align: center;font-size: 30px;font-weight: 300;color: #00a4ff;}.listItem{width: 90%;//height: 200px;margin: auto;/*background-color: pink;*/list-style: none;border-radius: 0 0 5px 5px;box-shadow: 1px 1px 5px 1px rgba(0,0,0,0.1),-1px -1px 5px 1px rgba(0,0,0,0.1);padding: 20px 0;box-sizing: border-box;}}
}
</style>
List.vue
<template><div><ul><div class="con"><Item v-for="todoObj in todos" :key="todoObj.id" :todo="todoObj"/></div></ul></div>
</template><script>
import Item from "@/components/Item";
export default {name:'List',components:{Item},props:['todos']
}
</script><style scoped lang="less">
ul{.con{//width: 95%;//margin: auto;border-bottom: 1px solid rgba(87, 87, 87, 0.3);border-left: 1px solid rgba(87, 87, 87, 0.3);border-right: 1px solid rgba(87, 87, 87, 0.3);margin: 0px 8px;//background-color: pink;}
}
</style>
Item.vue
<template><div><li>
<!-- 如下代码也能实现功能,但是不太推荐,因为有点违反原则,因为修改了props--><input type="checkbox" name="matter" id="" v-model="todo.done"> {{todo.title}}<button class="delete">删除</button></li></div>
</template><script>export default {name: "Item",//声明接收todo对象props:['todo'],}
</script><style scoped lang="less">
li{//height: 35%;//width: 96%;display: block;//background-color: pink;margin: auto;padding: 12px;border-top: 1px solid rgba(87, 87, 87, 0.3);//border-left: 1px solid rgba(87, 87, 87, 0.3);//border-right: 1px solid rgba(87, 87, 87, 0.3);//box-sizing: border-box;border-collapse: collapse;button{background-color: #d9534f;float: right;padding: 3px 10px;color: white;border: 1px solid #d43f3a;border-radius: 5px;cursor: pointer;&:hover{background-color: #c9302c;border: 1px solid #ac2925;}}&:hover{background-color: rgba(0,0,0,0.1);}
}
</style>