赞
踩
拆分静态组件:按照功能抽取组件,使用组件实现静态页面效果
注意:命名不要和html标签冲突
实现动态组件( 展示动态数据)
注意:所以data、props、methods、computed的数据命名不要重名
2.1 数据的类型、名称是什么?
数据的名称:Todos
数据的类型:对象数组
2.2 数据保存在哪个组件?
考虑好数据的存放数据,数据是一个组件在用还是一些组件在用
1. 一个组件在用:放在组件自身即可
2. 多个组件在用:放在他们共同的父组件上
实现交互——从绑定时间监听开始
将项目分为多个组件,分析其关系。
将之前用原生js做的TodoList项目ctrl+c
和ctrl+v
到相应的组件。基本实现静态组件。
效果
一堆数据(要做的事情) -> 数组
每一个数据(要做的事) -> 对象(具有不同的属性)
todos:[
{id:'001',title:"xxx",done:true},
{id:'002',title:"xxx",done:false},
...,
]
}
<UserItemtodo v-for="todoObj in todos" :key="todoObj.id" :todo="todoObj" />
根据数组的数组的数量去遍历用多少次UserItemtodo
组件,v-for:"(item,index) in todos"
,
把数组中每一个具体信息的对象传递给UserItemtodo
组件:DemoName:"item"
在 UserItemtodo
组件中声明接收传递过来的数据(todo对象)
props:['todo']
根据每个对象的信息按需求渲染页面
根据对象信息(done属性
)来控制复选框是否勾选(todo.done为true则选中,todo.done为false则不选中)<input type="checkbox" :checked="todo.done"/>
展示(每个要做的事情的名字): <span>{{todo.title}}</span>
添加一个todo
input type="text" placeholder="回车添加" @checked.enter="add">
add(event){ //event 发生事件的元素
console.log(e.target.value)
},
方法二:
双向数据绑定 <input type="text" placeholder="回车添加" @keyup.enter="add" v-model="title"> </div>
使用v-model
时要切记:v-model
绑定的值不能是props
传过来的值,因为props
是不可以修改的
data(){
return{
title:''
}
},
methods:{
add(e){
console.log(this.title}
},
}
我这里使用第一种方法。
add(e){
// 将获取到的名字包装成一个todo对象
const todoObj={id:''}
}
这里的id没有服务器和数据的支撑,需要生成一个独一无二的id作为辨识。
那如何生成不同的id呢?我们可以使用一个库nanoid
安装这个库npm i nanoid
//使用这个库
import {nanoid} from 'nanoid'//直接调用nanoid函数,它就能给你一个唯一的字符串
export default {
name:'UserHeader',
methods:{
add(e){
const todoObj={id:nanoid(),title:e.target.value,done:false}
},
}
}
4.将打包好的todo对象加入到todos数组中
回归到本质问题——数据保存在哪个组件?
如果todos数组保存在和MyHeader同级的MyList组件中,同级组件之间难以传输数据。
props
适用于:
父组件 == 子组件 通信
子组件==> 父组件 通信(要求父组件先给子组件一个函数)
因此todos数组应该保存在App
组件中
App.vue
this.todos.unshift(todoObj)
Vue捕获到的你对todos的修改,开始重新解析模板
<template> <div id="root"> <UserHeader :Addtodo="Addtodo"/>//将Addtodo函数传给UserHeader组件 <UserList :todos="todos"/>//将todos数组传给UserList组件 <UserFooter/> </div> </template> <script> import UserHeader from './components/UserHeader' import UserList from './components/UserList'//List组件包括Itemtodo组件和Itemdone组件 import UserFooter from './components/UserFooter' export default { name: 'App', components: { UserHeader,UserList,UserFooter }, data(){ return{ todos:[] } }, methods:{ Addtodo(todoObj){ //为什么不叫add()? 与UserHeader中的methods中的add函数重名了 //将接收到的todo对象加入到数组最前边 this.todos.unshift(todoObj) } } } </script>
UserList
组件
<template> <div class="container"> <div class="left"> <h3>To do</h3> <UserItemtodo v-for="todoObj in todos" :key="todoObj.id" :todo="todoObj" /> </div> <div class="right"> <h3>Done</h3> <UserItemdone v-for="todoObj in todos" :key="todoObj.id" :todo="todoObj"/> </div> </div> </template> <script> import UserItemtodo from './UserItemtodo' import UserItemdone from './UserItemdone' export default { name:'UserList', components:{ UserItemtodo, UserItemdone }, props:['todos'] } </script>
UserHeader
组件
UserHeader
组件里调用Vue
组件传递过来的函数,而函数的定义本身在App
组件中,App
就能接收到参数
<template> <div class="nav"> <input type="text" placeholder="要添加什么任务呢?(回车添加)" @keyup.enter="add" > </div> </template> <script> import {nanoid} from 'nanoid' export default { name:'UserHeader', props:['Addtodo'], methods:{ add(){ //校验数据 if(!e.target.value.trim()) return alert("输入不能为空") const todoObj={id:nanoid(),title:e.target.value,done:false} //通知App组件去添加一个todo对象 this.Addtodo(todoObj) //清空输入 e.target.value=''//回车添加一个事件后,清空输入框 }, } } </script>
<input type="checkbox" :checked="todo.done" />
<input type="checkbox" :checked="todo.done" @click="handlecheck(todo.id)"/>
or
<input type="checkbox" :checked="todo.done" @change="handlecheck(todo.id)"/>
函数
数据在哪里,操作数据的方法就在哪里
App
组件中写函数
methods:{
Addtodo(todoObj){
//添加一个todo
this.todos.unshift(todoObj)
},
//勾选or取消勾选一个todo
checktodo(id){
this.todos.forEach((todo)=>{//遍历todos数组中的每一个todo对象
//函数体
if(todo.id==id) todo.done=!todo.done
})
}
}
App
组件将函数传给UserList
组件,UserList
组件再把函数传给UserItemtodo
组件
(组件间通信的方法暂时只用此种)
UserIntemtodo
组件
props:['todo','checktodo'],
methods:{
handleCheck(id){
//通知App组件将对应的todo对象的done值取反
this.checktodo(id)
}
}
方法二:
input
框是checkbox
类型,并且v-model
绑定的是一个布尔值,那么这个v-model
就能够实现两个事:
1.初始化列表 维护好是否勾选
2.双向数据绑定 勾选与取消勾选会引起todo.done
的改变
<!-- 如下代码也能实现代码,但是不太推荐,因为有点违反原则,因为修改了props -->
<input type="checkbox" v-model="todo.done"/>
注意:Vue
只是很浅层的监视props
传递的数据是否修改
使用v-model时要切记:v-model
绑定的值不能是props
传过来的值,因为props
是不可以修改的
props
传过来的若是对象类型的值(地址没变),修改对象中的属性时Vue
不会报错,但不推荐这样做
2.2 根据todo
对象的done
属性值判断在对应位置是否显示(通过v-for
或v-if
实现)
UserItemtodo
组件
<div class="Item" v-show="!todo.done">
<label>
<input type="checkbox" :checked="todo.done" @change="handleCheck(todo.id)"/>
<span >{{todo.title}}</span>
</label>
<button class="btn" @click="handleDelete(todo.id)">删除</button>
</div>
UserItemdone
组件
<div class="Item" v-show="todo.done">
<label>
<input type="checkbox" :checked="todo.done" @change="handleCheck(todo.id)"/>
<span>{{todo.title}}</span>
</label>
<button class="btn" @click="handleDelete(todo.id)">删除</button>
</div>
效果:
1.鼠标悬浮有高亮效果
css
简单实现
.Item:hover{
background:rgba(0,0,0,0.30);
}
2.点击删除按钮获取事件id
methods:{
//勾选or取消勾选
handleCheck(id){
//通知App组件将对应的todo对象的done值取反
console.log(id)
this.checktodo(id)
},
//删除
handleDelete(id){
if(confirm('确定删除吗')){
console.log(id);
}
}
}
3.在App
组件中删除id对应的todo对象
App
组件中定义函数:
//删除一个todo
deletetodo(id){
this.todos=this.todos.filter(todo=>todo.id!==id)
//过滤掉不想要的,得到新数组赋值回原数组
}
App
组件将函数传给UserList
组件,UserList
组件再把函数传给UserItemtodo
组件
(组件间通信的方法暂时只用此种,其他方法以后再讲)
UserItemtodo
组件
//删除
handleDelete(id){
if(confirm('确定删除吗')){
this.deletetodo(id)
}
}
1.UserFooter
组件获取todos
数组
//App中
<UserFooter :todos="todos"/>``
//UserFooter中
props:['todos']``
2.数组数据按照需求渲染到页面
UserFooter
组件
<span class="done">已完成{{doneTotal}}</span>
<span class="all">全部{{todos.length}}</span>`
方法一:
computed:{
doneTotal(){
let i=0;
this.todos.forEach((todo) => {
if(todo.done) i++;
});
return i;
}
}
方法二:
使用reduce
函数
const x=this.todos.reduce((pre,cur)=>{//函数调用次数=数组长度
//console.log(pre,cur);//cur 每一个todo项
return pre+(cur.done?1:0)
},
0)//0为pre的初始值
return x;
精简代码
computed:{
doneTotal(){
return this.todos.reduce((pre,todo)=>pre+(todo.done ?1 : 0),0)
}
}
todos
数组长度就是全部的数量
todos
数组中todo.done=true
的数量就是已完成的数量
1.1 优化事件全部数量的显示方法(全部事件等于0时不显示)
<div class="footer" v-show="total">
<input type="checkbox" :checked="doneTotal==total"/>全选完成
total(){
return this.todos.length
},
1.2 如果todo
的done
值为真,全选复选框勾选(且事件个数大于0)
<input type="checkbox" :checked="isAll"/>全选完成
isAll(){
return this.doneTotal===this.total && this.total>0
}
1.3 点击全选复选框,将所有todo
的done
属性设为true
App
组件
定义一个全选函数
//全选or取消全选
checkAlltodo(done){
this.todos.forEach((todo)=>{
todo.done=done
})
}
方法一:
UserFooter
组件
获取复选框是否勾选,接收App
传递的函数,调用函数
<input type="checkbox" :checked="isAll" @change="checkAll"/>全选完成
isAll(){
return this.doneTotal===this.total && this.total>0
}
checkAll(e){
this.checkAlltodo(e.target.checked);
}
方法二:
:checked="isAll" @change="checkAll"
初始化展示+交互->v-model
`UserFooter``
<input type="checkbox" v-model="isAll"/>全选完成
出现错误
isAll
函数使用简写方式时,isAll
只能被定义而不被修改
UserFooter
组件
<input type="checkbox" v-model="isAll"/>全选完成
isAll:{//自己计算出来的,不是props传递过来的数据
get(){//定义获取
return this.doneTotal===this.total && this.total>0
},
set(value){//修改
this.checkAlltodo(value);
}
}
App
组件
//清除所有已经完成的todo
clearAlltodo(){
this.todos=this.todos.filter(todo=>!todo.done)
}
App
组件将clearAlltodo
函数传递给UserFooter
组件
UserFooter
组件
把todos
数组中的todo.done=true
的`对象全部删除
<button class="btn btn-danger" @click="claerAll">清除已完成任务</button>
claerAll(){
this.clearAlltodo();
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。