「后端小伙伴来学前端了」Vue中利用全局事件总线改造 TodoList 案例
前言
上上篇写了:👉Vue
中利用Props
实现TodoList
案例
上篇写了:👉Vue
中全局事件总线的概念及基本使用
这篇就打算用全局事件总线来改造一下之前写的TodoList案例,一天学习一点,我们一起进步冲。
一、案例效果
需要实现的东西,和之前是一样的,只是我们换成用全局事件总线来进行组件之间的通信。
二、分析为什么要换成全局事件总线
为什么需要换成全局事件总线勒?我们拿Props也能够实现这些功能啊,可以是可以实现,但是我们看看之前有哪些问题的存在。
我们之前在App组件中套入了一个List组件,然后在List组件套了一个Item组件,数据定义在App组件,就意味着我们要实现祖孙组件之间通信。
props实现如下:
一直传递到Item组件中才使用
作为咱们程序员来说,在一个组件中,写了但是又完全没有使用的东西,就是多余的哈。
不过尤雨溪大佬已经替我们懒完了,就有了这些全局事件总线啊,还有Vuex这种生态,来方便我们进行组件通信。
使用全局事件总线就可以解决这个问题,更方便实现祖孙组件之间通信。
三、全局事件总线实现TodoList
我们着重于实现app组件和Item组件之间的通信,也就是祖孙组件之间的通信哈。
另外兄弟组件也是一样的实现方法哈,实现起来,再也不用像props那样多层传递了,也不用再借助中间层拉,直接绑定即可以通信拉。
祖孙组件之间通信
App组件:
<template>
<div class="todo-container">
<!-- header模块 -->
<TodoHeader/>
<!-- main 模块 -->
<TodoList :todos="todos"/>
<!-- 主要的内容模块 -->
<TodoFooter :todos="todos" :clearDoneTodos="clearDoneTodos" :checkAllTodos="checkAllTodos" />
<!-- footer模块 -->
</div>
</template>
<script>
import TodoHeader from './components/MyTodoHeader'
import TodoList from './components/MyTodoList'
import TodoFooter from './components/MyTodoFooter'
export default {
components: {
TodoHeader,
TodoList,
TodoFooter
},
data () {
return {
todos: [
{ id: '001', title: '吃饭', done: true },
{ id: '002', title: '睡觉', done: false },
{ id: '003', title: '敲代码', done: true }
]
}
},
methods: {
// 回车增加一个todo
addTodo (todo) {
this.todos.unshift(todo)
},
// 判断勾选不勾选
checkTodo (id) {
this.todos.forEach((todo) => {
if (todo.id === id) todo.done = !todo.done
})
},
// 删除一个todo
deleteTodo (id) {
this.todos = this.todos.filter(todo => todo.id !== id)
},
// 全选全不选
checkAllTodos (done) {
this.todos.forEach((todo) => { todo.done = done })
},
// 清除所有已完成的任务
clearDoneTodos () {
this.todos = this.todos.filter(todo => !todo.done)
}
},
// 在加载完成后就进行全局总线的绑定
mounted () {
this.$bus.$on('addTodo', this.addTodo)
this.$bus.$on('checkTodo', this.checkTodo)
this.$bus.$on('deleteTodo', this.deleteTodo)
},
// 养成习惯 在组件销毁的时候,将事件进行解绑
beforeDestroy () {
this.$bus.$off('addTodo')
this.$bus.$off('checkTodo')
this.$bus.$off('deleteTodo')
}
}
</script>
<style>
*{
margin: 0 0;
padding: 0 0;
}
.todo-container{
margin:0 auto;
margin-top: 10px;
width: 500px;
height: 500px;
background-color: #CCC;
border: 1px solid #ddd;
border-radius:8px;
}
</style>
List组件:
<template>
<ul class="todo-main">
<TodoItem v-for="(todo, index) in todos" :key="index" :todo="todo" />
</ul>
</template>
<script>
import TodoItem from './MyTodoItem'
export default {
components: {
TodoItem
},
props: {
todos: Array
},
methods: {
}
}
</script>
<style scoped>
/*main*/
.todo-main {
margin-top: 10px;
margin-left: 0px;
border: 1px solid #ddd;
border-radius: 2px;
padding: 0px;
}
.todo-empty {
height: 40px;
line-height: 40px;
border: 1px solid #ddd;
border-radius: 2px;
padding-left: 5px;
margin-top: 10px;
}
/*item*/
li {
list-style: none;
height: 36px;
line-height: 36px;
padding: 0 5px;
border-bottom: 1px solid #ddd;
}
li label {
float: left;
cursor: pointer;
}
li label li input {
vertical-align: middle;
margin-right: 6px;
position: relative;
top: -1px;
}
li button {
float: right;
display: none;
margin-top: 3px;
}
li:before {
content: initial;
}
li:last-child {
border-bottom: none;
}
</style>
MyTodoItem组件
<template>
<li @mouseenter="handleEnter(true)" @mouseleave="handleEnter(false)" :style="{background: bgColor}">
<label>
<input type="checkbox" :checked="todo.done" @click="handlerCheck(todo.id)" />
<span>{{todo.title}}</span>
</label>
<button class="btn btn-danger" style="display:none" v-show="isShow" @click="handlerDeleteItem(todo.id)">删除</button>
</li>
</template>
<script>
export default {
props: {
todo: Object
},
data () {
return {
bgColor: 'white',
isShow: false
}
},
methods: {
handleEnter (isEnter) {
if (isEnter) {
this.bgColor = '#aaa'
this.isShow = true
} else {
this.bgColor = 'white'
this.isShow = false
}
},
// 修改勾选状态
handlerCheck (id) {
console.log(id)
// this.checkTodo(id)
// 使用全局事件 总线 不再需要麻烦中间组件 list 就可以做到直接通信
this.$bus.$emit('checkTodo', id)
},
handlerDeleteItem (id) {
if (window.confirm('确定删除吗')) {
// this.deleteTodo(id)
this.$bus.$emit('deleteTodo', id)
}
}
}
}
</script>
这样就省去了MyList
组件去做中间层。另外代码及结构也会显得更加的清晰。
兄弟组件之间通信
现在我们的需求是需要在Footer组件中点击修改按钮,然后能够做到修改List组件下的Item组件的值(宁在春的这个值
)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yAte2u8c-1637166284627)(C:\Users\ASUS\Desktop\宁在春的学习笔记\前端系列\前端学习笔记\09Vue中利用全局总线实现TodoList案例.assets\image-20211118001654427.png)]
item组件
<template>
<li
@mouseenter="handleEnter(true)"
@mouseleave="handleEnter(false)"
:style="{ background: bgColor }"
>
<label>
<input
type="checkbox"
:checked="todo.done"
@click="handlerCheck(todo.id)"
/>
<span>{{ todo.title }}</span>
//这里就是展示的数据
<span>{{ msg }}</span>
</label>
<button
class="btn btn-danger"
style="display: none"
v-show="isShow"
@click="handlerDeleteItem(todo.id)"
>
删除
</button>
</li>
</template>
<script>
export default {
props: {
todo: Object
},
data () {
return {
bgColor: 'white',
isShow: false,
msg: ' 宁在春'
}
},
methods: {
handleEnter (isEnter) {
if (isEnter) {
this.bgColor = '#aaa'
this.isShow = true
} else {
this.bgColor = 'white'
this.isShow = false
}
},
// 修改勾选状态
handlerCheck (id) {
console.log(id)
// this.checkTodo(id)
// 使用全局事件 总线 不再需要麻烦中间组件 list 就可以做到直接通信
this.$bus.$emit('checkTodo', id)
},
handlerDeleteItem (id) {
if (window.confirm('确定删除吗')) {
// this.deleteTodo(id)
this.$bus.$emit('deleteTodo', id)
}
},
updateMsg () {
this.msg = '大家好啊,我叫宁在春'
}
},
mounted () {
//绑定事件 后面跟着的this.updateMsg 是自己定义在 methods 中的方法
this.$bus.$on('updateMsg', this.updateMsg)
},
// 养成习惯 在组件销毁的时候,将事件进行解绑
beforeDestroy () {
this.$bus.$off('updateMsg')
}
}
</script>
<template>
<div class="todo-footer" v-show="total">
<label>
<!-- // 第一种方式:通过dom元素来判断有没有进行勾选 不是最佳方式 -->
<!-- <input type="checkbox" :checked="isAllCheck" @click="checkAll" /> -->
<!-- 第二种方式: 通过绑定计算属性来进行展示 -->
<input type="checkbox" v-model="isAllCheck" />
</label>
<span
>已完成{{ doneTotal }}<span> / 全部{{ todos.length }} </span>
</span>
<button
class="btn btn-danger"
@click="deleteDoneAll"
>
清除已完成任务
</button>
//点击修改
<button @click="updateMessag">点击修改</button>
</div>
</template>
<script>
export default {
props: {
todos: Array,
clearDoneTodos: Function,
checkAllTodos: Function
},
computed: {
total () {
return this.todos.length
},
doneTotal () {
return this.todos.reduce((preTotal, todo) => preTotal + (todo.done ? 1 : 0), 0)
},
isAllCheck: {
get () {
return this.doneTotal === this.todos.length && this.doneTotal > 0
},
// 通过计算属性来判断是否全选或全不选
set (checked) {
this.checkAllTodos(checked)
}
}
},
methods: {
deleteDoneAll () {
this.clearDoneTodos()
},
updateMessag () {
//执行之前绑定的回调函数
this.$bus.$emit('updateMsg')
}
}
}
</script>
实现效果
四、源码
后语
大家一起加油!!!如若文章中有不足之处,请大家及时指出,在此郑重感谢。
纸上得来终觉浅,绝知此事要躬行。
大家好,我是博主
宁在春
:主页一名喜欢文艺却踏上编程这条道路的小青年。
希望:
我们,待别日相见时,都已有所成
。