也可以按照其他的划分,我是按照功能划分的
创建文件夹,终端进入目录下创建项目
vue create todolist
在组件文件夹下创建四个文件,分别是MyTop.vue;List.vue;Item.vue;MyFooter.vue;
并将四个组件引入App.vue
注意:Top.vue不要写Header.vue,容易和html的标签冲突
后来我的Top.vue也报错了,我改长一点,MyTop,就正常了
注册组件
export default {
name: 'App',
components: { MyTop,MyList,MyFooter}
}
因为item是list的子组件所以item注册在list里面就好啦
-
待办事件的数据需要数组来进行存储,id标识,数组保存在List.
在MyList中的MyItem子组件中遍历todos数组,给MyItem传数据。
在MyItem中设置props设置传值
//声明接收todo对象
props:['todo'],
checkbox添加checked就会在页面中显示勾选,动态的拥有某个属性,
:checked="true"
在表达式通过改变true来实现是否勾选
使其=todo.done即可
MyItem.vue
-
MyList.vue
在Top中将用户的输入包装成一个todo对象,将用户的输入todoObj添加到list当中
两个兄弟组件之间传递数据的思路:父组件App中有todos数组(将list中的数据复制到app中),将top中的todoObj传给todos,再通过props将todos中的数据传给list。
props:['addTodo'],
父组件传给子组件一个函数,子组件合适的时候调用。
top传给list数据的整个过程
App中操作todos中的数据unshift可以捕获到数组的修改,todos改变,vue重新解析模板,此时todos增加一个数据,todo数组传给mylist,mylist会重新解析模板,myitem出现四个虚拟dom,dom比较,旧的复用,新的添加。
e.target.value=''
但是这个语句直接操作DOM了,违背了Vue的原则。
input框添加v-model可以避免这个问题
//在methods里面添加
this.title=''
if (!this.title.trim()) return alert('输入不能为空')
勾选与否引起数据的变化,才可以显示完成率,产生交互.
通过勾选找到数据的id,在todo里面找到,然后通知app组件将对应的todo对象的done值取反。
首先需要input框显示勾选的todo(初始化交互)
:checked="todo.done"
绑定事件响应是否勾选(click/change都可以)
@click="handleCheck(todo.id)
可以通过点击输出勾选事件的id
console.log(id)
app组件中,通知app组件将对应的todo对象的done值取反
checkTodo(id) {
// 遍历list中的item
this.todos.forEach((todo) => {
if (todo.id === id) todo.done=!todo.done
})
}
组件间通信
本次采用逐次传递。
在App中将checkTodo传递给MyList组件,在MyList组件中使用props接收checkTodo
props:['todos','checkTodo'],
因为是Myitem使用,所以在MyList组件中的MyItem中单项数据绑定
:checkTodo="checkTodo"
在MyItem中使用props接收checkTodo。
MyItem中input框使用v-model双向数据绑定v-model=“todo.done”,通过布尔值决定input的checkbox是否勾选
props传值todo,todo的属性值修改,vue监测的就是todo属性
但是props的值不允许修改
看一下下面的例子
let a=3;
a=8;
修改了
let obj={a:2,b:3}
obj.=666
obj={x:100,y:200}
vue只能监测到obj={x:100,y:200}。
在本项目中myItem中添加
props接收a
但是这种方法并不推荐,因为有点违反原则,修改了props,vue监测不到。
在item中给按钮button添加点击事件handleDelete,注意接受todo的id
handleDelete(id) {
// 删除
if (confirm('确定删除吗?')) {
// console.log(id)
}
}
deleteTodo(id) {
this.todos = this.todos.filter((todo) => {
return todo.id!==id
})
}
再次经历爷孙传值
先App传给MyList,MyList中props接收deleteTodo,MyItem标签中单项数据绑定deleteTodo。之后MyList传给MyItem,item的props接收即可。
props: ['todo', 'checkTodo','deleteTodo'],
handleDelete(id) {
// 删除
if (confirm('确定删除吗?')) {
// console.log(id)
this.deleteTodo(id)
}
}
首先将todos传给myitem
//App.vue
//MyFooter.vue
props:['todos']
使用计算属性computed实现底部统计
已完成{{doneTotal}}/全部{{ todos.length }}
computed: {
doneTotal() {
let i = 0
this.todos.forEach((todo) => {
if(todo.done)i++
});
return i
}
}
底部统计也可以使用ES6的reduce方法完成一种更为高级的写法
const x= this.todos.reduce((pre,cur) => {
console.log('@', pre, cur)
return pre + (cur.done?1:0)
}, 0)
console.log('###', x)
return x
精简为一句代码
return this.todos.reduce((pre,cur)=>pre+(cur.done?1:0),0)
pre上一个值,cur当前的值,数组的长度就是函数的调用次数.
打印pre,0,undefined,undefined。统计的初始值是0,所以第一次调用是0,再调用的时候,pre是第一次调回的函数的返回值,第一次调用没有返回值,所以第二次和第三次都是undefined。如果有return 3,那么第二次和第三次pre都是3.所以返回值应该是return pre+1.这样第一次返回值是1,第二次是2,第三次是3。所以最后3就是reduce的返回值。
全部{{ total}}
computed: {
total() {
return this.todos.length
},
}
当任务为0时,不显示底部
checkAllTodo(done) {
this.todos.forEach((todo) => {
todo.done=done
})
}
Myfooter.vue中props接收传值,然后方法调用
props: ['todos','checkAllTodo'],
methods: {
checkAll(e) {
console.log(e.target.checked)//借助属性拿到Dom没有操作DOM
this.checkAllTodo(e.target.checked)
}
}
v-model替换
点击勾选框,[Vue warn]: Write operation failed: computed property “isAll” is readonly.
将isAll改为计算属性
isAll(){}改成isAll: {}
思路:给清空已完成的按钮设置click事件clearAll
methods设置方法clearAll,clearAll调用App入口文件的clearAllTodo方法,给myfooterclearAllTodo传给MyFooter组件
clearAllTodo() {
this.todos=this.todos.filter((todo) => {
return !todo.done
})
}
MyFooter文件使用props接收文件。
## 清空已完成任务
> 思路:给清空已完成的按钮设置click事件clearAll
methods设置方法clearAll,clearAll调用App入口文件的clearAllTodo方法,给myfooterclearAllTodo传给MyFooter组件
clearAllTodo() {
this.todos=this.todos.filter((todo) => {
return !todo.done
})
}
MyFooter文件使用props接收文件。