从4.15开始到现在已经5天了,回顾在腾讯课堂学习的点滴,颇有收获,现分享给大家。首先要感谢腾讯课堂推出的十天训练营活动,在训练营里用VUE来开发,正如她所说的是一款易用,灵活,高性能的渐进式框架,下面来看看她的真实面容。
需求:做一个todolist,要求可添加,删除,双击即可编辑,列表内容分为所有任务,未完成的任务,完成的任务,当没有任务内容显示提示,当有任务内容时,提示还有几条未完成。
效果图如下
1.功能简析
1.添加,编辑,删除
1.在添加任务的input框内回车可以添加一条任务,使用v-model监视输入框的Value,输入完毕后监控回车按键事件,利用事件监控器v-on和事件修饰符.enter可以完成。里面涉及到列表渲染v-for,每次输入完毕后的数据都要加入嵌入一个<li>标签并使内容为用户输入的数据来完成。
2.任务列表单选框选中时划掉任务,代表已完成,不选中代表未完成。同样还是用v-model,但是这时是监控数据的状态,我们定义了数据的isChecked属性的布尔值来判断是否选中。
3.双击任务时可以编辑任务,这里用到动态class,通过比较item和当前正在编辑的数据editTodos的最终结果布尔值来控制lediting样式是否显示,使原有任务内容隐藏,并且使输入框显示,事实上当你双击时这结果值是true。而这个editTodos则是在双击时触发事件,使之等于当前编辑的item。
:class="{lediting:item === editTodos}"
4.上一条还涉及到双击需要用户还需要一次点击来获取光标以便编辑内容,现在我们怎么让用户双击后光标直接显示。自定义一个指令v-focus来操作原生dom,在vue里面提供了几个钩子函数来操作,这次用到update
被绑定元素所在的模板更新时调用,通过比较更新前后的绑定值,可以忽略不必要的模板更新:
v-focus="editTodos === item"
5.当用户在编辑任务后输入框失去焦点或者回车结束编辑,同样用到事件监控器,跟前面的一样,只需要使得editTodos数据为空,定义的样式失效,任务内容显示,并且使输入框隐藏;如果用户发现编辑后想退回原来的任务内容,这里我们定义了按键事件keyup.esc来返回第一次双击要编辑的内容。
6.实现删除任务,点击按钮触发删除操作,对输入数据的数组删除操作,在v-for里面我们用(item,index) in list 来渲染列表,删除时对list使用splice操作。
上面对列表的添加,编辑,删除等做出了简要分析,下面主要对任务内容,包括所有任务,未完成任务,完成的任务显示相应的内容做出简要分析。
2.任务列表内容分类
1.在所列出三个选项<a>标签中,我们设置了它的href值分别为#all,#unfinished,#finished。这次不使用v-router来操作,而是给window添加一个事件“hashchange",当hash值变化来过滤任务列表。
我们获取到hash值存储在vm实例的数据选项里一个叫做visibility的属性值上,
function watchHashChange(){
var hash = window.location.hash.slice(1);
vm.visibility = hash;
}
window.addEventListener("hashchange",watchHashChange);
然后对list列表进行筛选,也就是过滤器,前面说到isChecked表示任务数据的状态值,这个过滤器的使用方法是根据回调函数的返回值(或者说是过滤条件来返回符合条件的选项)
list.filter(function(item){
return item.isChecked
})
我们定义了一个过滤后的数据filteredList用来给v-for渲染列表替代list。
filteredList:function(){
//过滤的时候有三种情况 all finished unfinished
var filter ={
all:function(list){
return list
},
finished:function(list){
return list.filter(function(item){
return item.isChecked
})
},
unfinished:function(list){
return list.filter(function(item){
return !item.isChecked
})
}
}
//找到了过滤函数,就返回过滤后的数据,如果没有,就返回所有数据
return filter[this.visibility]?filter[this.visibility](list):list;
}
}
3.用本地存储loaclStorage来存储用户输入的值
前面一直说到list值,其实这个值更符合现实的是用户输入的数据,而不是我们定义的数据。我们封装localStorage提供的setItem和getItem的方法。key是定义本地存储的数据库的名字,value是用户输入的数据需要将其转换为json格式,当取出时需要解析为js对象。
var store = {
save(key,value){
localStorage.setItem(key,JSON.stringify(value));
},
fetch(key){
return JSON.parse(localStorage.getItem(key)) || [];
}
};
2. 2个用vue时需要注意的细节
1.前面一直用到事件监控器来监控键盘事件,鼠标事件,在触发时调用的函数,如果你在模板里没有传参,而你在vue选项对象的methods中写的方法函数写了形参则是ev事件对象,如果你在模板里有传参,后面还需要操作事件对象,需要你在模板里手动传入$event,在methods中的形参才有ev事件对象,没有手动传则不能使用。实际上vue在避免使用原生的ev对象,因为vue是数据驱动的。
2.在localStorage中,涉及深复制这概念,比如用户选中或者取消选中单选框,如果没有deep:true;数据状态的改变并不会记录下来,当你刷新页面时,还是未操作之前的状态。
3.核心代码
function watchHashChange(){
var hash = window.location.hash.slice(1);
vm.visibility = hash;
}
window.addEventListener("hashchange",watchHashChange);
//存取localStorage中的数据
var store = {
save(key,value){
localStorage.setItem(key,JSON.stringify(value));
},
fetch(key){
return JSON.parse(localStorage.getItem(key)) || [];
}
};
//取出所有的值
var list = store.fetch("input-new-data");
var vm = new Vue({
el:'.main',
data:{
list:list,
todo:'',
editTodos:'', //记录正在编辑的数据
beforeTitle:'', //记录正在编辑的数据的title
visibility:"all" //通过这个属性值的变化对数据进行筛选
},
directives:{
"focus":{
update(el,binding){
if(binding.value){
el.focus();
}
}
}
},
watch:{
list:{
handler:function(){ //监控list这个属性,当属性对应的值发生变化就会执行函数
store.save("input-new-data",this.list)
},
deep:true
}
},
computed:{
noChecked(){ //有几个任务未完成
return this.list.filter(function(item){
return !item.isChecked
}).length
},
filteredList:function(){
//过滤的时候有三种情况 all finished unfinished
var filter ={
all:function(list){
return list
},
finished:function(list){
return list.filter(function(item){
return item.isChecked
})
},
unfinished:function(list){
return list.filter(function(item){
return !item.isChecked
})
}
}
//找到了过滤函数,就返回过滤后的数据,如果没有,就返回所有数据
return filter[this.visibility]?filter[this.visibility](list):list;
}
},
methods:{
addTodo(){ //添加任务
//向list中添加一项任务 事件处理函数中的this指向vm
this.list.push(
{
title:this.todo,
isChecked:false
}
),
this.todo=''
},
deleteTodo(item,index){ //删除任务
this.list.splice(index,1);
},
editTodo(item){ //编辑任务
this.editTodos = item,
this.beforeTitle = item.title
},
editedTodo(){ //失去焦点后的编辑任务
this.editTodos = ''
},
cancelTodo(item){ //取消编辑任务
item.title=this.beforeTitle,
this.beforeTitle='',
//让div显示,input隐藏
this.editTodos = ''
}
}
});