Vue基础11
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:{
//添加一个todo
addTodo(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:{
//添加一个todo
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>
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>