vue项目todolist
初始化项目
(1)先把vue项目创建出来
(2)然后把src中的文件替换掉,替换成我们项目的文件
(3)创建3个组件,头部组件(TodoHeader),身体组件(TodoMain),结尾组件(TodoFooter)
(4)把样式导入App.vue中去
(5)把数据列表传入进去

TodoMain获取父组件数据,进行传值
父组件使用自定义属性,动态把list数组传递给子组件
子组件使用props进行接收,然后使用v-for对页面进行渲染
App.vue使用自定义属性,传递参数
<TodoMain :list="list"></TodoMain> //父传子
TodoMain使用props进行接收,然后使用v-for对页面进行渲染
//子组件对父组件进行接收 props : { list : { type : Array, required : true } } //子组件使用v-for对页面进行渲染 <!-- 当任务已完成,可以给 li 加上 completed 类,会让元素加上删除线 --> <li :class="{completed : item.isDone}" v-for="(item,index) in list" :key="item.id" > <div class="view"> <input class="toggle" type="checkbox" v-model="item.isDone"/> <label>{{ item.name }}</label> <button class="destroy"></button> </div> </li>

删除功能
子组件不能直接对父组件传过来的数据进行修改,所以子组件需要把方法传递给父组件,父组件完成删除操作
TodoMain组件创建事件,并且暴漏给父组件
<button class="destroy" @click="del(item.id)"></button> //子组件创建事件,暴漏给父组件 methods : { del (id) { // console.log(id) this.$emit(`del`,id) } }
父组件进行接收,并且为子组件提供一个方法
<TodoMain :list="list" @del="fuDel"></TodoMain> //父组件接收子组件的传值 //父组件为子组件创建一个方法 methods : { fuDel (id) { this.list = this.list.filter(i => { return i.id !== id }) } }

点击选中按钮,进行删除线绑定
因为我们使用的是v-model进行删除线绑定的,违反了,子组件不能修改父组件原则,所以我们使用v-bind进行数据绑定
然后使用v-on:change事件对按钮进行监听,然后使用父组件传递数据给子组件
TodoMain子组件重新进行绑定,并且传值给父组件
<input class="toggle" type="checkbox" :checked="item.isDone" @change="change(item.id)"/> change(id) { // console.log(id) this.$emit(`change`,id) //子组件进行传值 }
App.vue父组件进行接收,并且对isDome状态进行修改
<TodoMain :list="list" @del="fuDel" @change="fuChange"></TodoMain> fuChange (id) { // console.log(id) //拿到状态以后遍历数组,当我们点击的时候对属性进行切换 this.list.forEach(i => { //这里我们已经动态绑定checked属性,当item.isDone为true的时候,添加删除线 if (i.id === id) { //判断是否是我们点击的id值 i.isDone = !i.isDone //我们让i.isDome取反,然后传递给动态的checked事件 } }) }
TodoHeader输入进行添加
我们先获取到输入框中的数值,然后子组件传递给父组件,父组件进行添加
TodoHeader我们首先绑定一个v-model获取表单框中输入的信息,然后创建回车添加事件,暴漏给父级
<input class="new-todo" placeholder="请输入任务名称" autofocus v-model.trim="name" @keyup.enter="tian" /> data () { return { name : `` //在data中声明一个name属性,获取表单中的数值 } }, methods : { tian () { // console.log(this.name) //获取到添加的数据 this.$emit(`tian`,this.name) this.name = `` //把name更换成为控制,每次回车以后,确保输入框内容被清空掉了 } }
App.vue使用自定义属性进行接收,然后在methods添加子组件传递过来数据
<TodoHeader @tian="fuTian"></TodoHeader> fuTian (name) { // console.log(name) this.list.push({ id: Date.now(), name : name, //这里前边一个是list中的name属性,后边的是传入的name的属性 isDone: false }) }

TodoFooter,尾部组件进行修改
TodoFooter,获取父组件的传值,对剩余项目的判断
TodoFooter首先接收父亲的传值
TodoFooter接收父亲的传值
props : { list : { type : Array, required : true } },
App.vue进行对子组件的传值
<TodoFooter :list="list"></TodoFooter>
TodoFooter然后使用computed监视已经完成的数组的长度,然后动态渲染未完成的到页面
最后判断数组的长度是否为,如果数组长度为0的情况下隐藏底部
<footer class="footer" v-if="show"> //如果数组长度为0,页面底部属性隐藏 <span class="todo-count"><strong>{{ length > 0 ?`剩余 ${length}` : `全部完成`}}</strong></span> computed : { //创建监视属性,因为我们不需要对页面进行修改,可以节省创建 length () { return this.list.filter(i => i.isDone === false).length //求出未完成数组的长度 }, show () { // console.log() return this.list.length > 0 ? true : false } }

TodoFooter显示隐藏清除全部,清空按钮清空功能
TodoFooter如果我们没有已完成的,不显示清除全部按钮,如果我们有已完成的隐藏显示按钮
<button class="clear-completed" v-if="qing">清除已完成</button> qing () { //当我们任何一项的is.Done===true的时候我们就显示按钮,反之如果我们所有项false的时候就隐藏按钮 return this.list.some(i => i.isDone === true) }
TodoFooter清除我们已经完成的项,添加点击事件,然后暴漏给父组件,父组件获取以后进行数组修改
<button class="clear-completed" v-if="qing" @click="Del">清除已完成</button> methods : { Del () { // console.log(`清除`) this.$emit(`Del`) } }
App.vue父组件进行接收子组件传值,并且进行修改
<TodoFooter :list="list" @Del="FuDel"></TodoFooter> FuDel () { this.list = this.list.filter(i => { return i.isDone !== true }) }

TodoFooter底部高亮效果
首先添加点击事件,然后添加动态属性,最后添加type属性,进行对比,如果为true显示,为false的时候进行隐藏
首先我们的红色外边框属性是selected,我们使用动态绑定,绑定class属性,当class属性为true的时候,我们让他显示出来
动态决定是否为true
我们在data中创建一个type属性,给type属性设置一个all的默认值,然后判断,动态class属性判断为true的时候显示出来红色边框
最后我们添加一个点击事件,每次点击的时候都会给data传递一个参数,进行判断,让calss属性为true或者false
所以每次我们点击的时候,会把参数传递给data的type属性,然后使用type属性和我们的定义的值进行对比,当对比为true的时候边框显示,false的时候边框不显示
<ul class="filters">
<li>
<a :class="{selected : type === `all`}" href="#/" @click="color(`all`)">全部</a>
</li>
<li>
<a :class="{selected : type === `active`}" href="#/active" @click="color(`active`)">进行中</a>
</li>
<li>
<a :class="{selected : type ===`completed`}" href="#/completed" @click="color(`completed`)">已完成</a>
</li>
</ul>
data () {
return {
type : `all` //创建type属性
}
},
methods : {
Del () {
// console.log(`清除`)
this.$emit(`Del`)
},
color (type) { //点击事件,获取点击的参数,然后使用参数给data数据进行赋值
this.type = type
}
}

TodoFooter状态提升,让父组件管理点击事件,然后可以让别的组件也获取到点击信息
将type状态提升到父组件中
首先App.vue父组件创建type属性传值给子组件,然后子组件进行接收
<TodoFooter :list="list" @Del="FuDel" @Color="color" :type="type"></TodoFooter> data () { return { list: [ { id: 1, name: '读万卷书', isDone: true }, { id: 2, name: '行万里路', isDone: false }, { id: 3, name: '学而时习之', isDone: true }, ], type : `all` } },
TodoFooter子组件进行接收(记得注销掉data中的type属性,因为以后就使用父组件中的type属性)
props : { list : { type : Array, required : true }, type : { type : String } },
TodoFooter子组件进行传值给父组件实现点击事件
color (type) { //点击事件,获取点击的参数,然后使用参数给data数据进行赋值
// this.type = type
this.$emit(`Color`,type)
}
App.vue父组件进行处理,然后返回给子组件
<TodoFooter :list="list" @Del="FuDel" @Color="color" :type="type"></TodoFooter> color (type) { this.type = type }

TodoMain完成下边点击以后的过滤功能
在TodoMain中判断isDome是否为true或者false,然后显示出来,使用新值进行遍历渲染页面
我们首先获取到App.vue父组件的type值,父组件先给子组件进行传值
<TodoMain :list="list" @del="fuDel" @change="fuChange" :type="type"></TodoMain>
然后子组件进行接收,并且进行判断,渲染的数据
props : { list : { type : Array, required : true }, type : { //接收父组件的传值 type : String } },
判断父组件传递过来的数值,我们重新赋值,然后对页面进行判读渲染
computed : { xinList () { if (this.type === `completed`) { //显示已经完成的 return this.list.filter(i => i.isDone === true) } else if (this.type === `active`) { //显示未完成的 return this.list.filter(i => i.isDone === false) } else { //剩余的就是全部数组 return this.list } } }
最后我们把新的数组数据渲染到页面上边
<li :class="{completed : item.isDone}" v-for="(item,index) in xinList" :key="item.id" >
本地储存功能
我们把数据先储存到本地,然后父组件重新获取本地的数据,对子组件进行传值
我们在App.vue父组件中进行动态监视,监视每次数据的变化进行储存
watch : {
list : {
handler (value) {
// 可以判断数据是否更改,当数据进行更改的时候,进行储存
// 先转换数据类型为字符串类型,然后储存到名字叫做todo的本地缓存的值中
localStorage.setItem(`todo`,JSON.stringify(value))
},
deep : true //开启深度监听,可以监听数组
}
}
我们每次获取数据的时候,直接在网页中进行获取
data () { return { // list: [ // { id: 1, name: '读万卷书', isDone: true }, // { id: 2, name: '行万里路', isDone: false }, // { id: 3, name: '学而时习之', isDone: true }, // ], // 每次获取list数据的时候,都是从本地获取,如果说没有数据,就返回一个空的数组 list : JSON.parse(localStorage.getItem(`todo`)) || [], type : `all` } },

TodoHeader全部选中功能,(双向数据绑定)
栓修改那个数据绑定,然后添加监视事件
<input id="toggle-all" class="toggle-all" type="checkbox" v-model="quan" />
computed : {
xinList () {
if (this.type === `completed`) { //显示已经完成的
return this.list.filter(i => i.isDone === true)
} else if (this.type === `active`) { //显示未完成的
return this.list.filter(i => i.isDone === false)
} else { //剩余的就是全部数组
return this.list
}
},
quan () {
return this.list.every(i => i.isDone === true)
}
然后传递给父组件让父组件进行双向数据绑定和修改
<input id="toggle-all" class="toggle-all" type="checkbox" v-model="quan" /> quan : { get () { return this.list.every(i => i.isDone === true) }, set (value) { this.$emit(`quan`,value) } }
<TodoMain :list="list" @del="fuDel" @change="fuChange" :type="type" @quan="quan"></TodoMain> quan (value) { this.list.forEach((item) => (item.isDone = value)) }


浙公网安备 33010602011771号