1. 环境搭建
1.1. 创建项目
npm install -g @vue/clivue create vue_study_todolist
1.2. 清空项目代码
清楚HelloWorld.Vue
代码中的内容。
1.3. 启动空项目
1.4 项目目标
项目组件实现以下效果
2. 组件拆分代码
Vue是一个基于组件的框架,允许您将界面拆分成小的、可重用的组件。每个组件都可以包含自己的模板、样式和逻辑,从而提高了代码的可维护性和重用性。
组件通常通过props和events来进行通信。Props允许父组件向子组件传递数据,而子组件则通过events将数据的变化通知给父组件。这种单向数据流的模式使得应用程序的数据流更加可控和预测。
在Vue中,您可以在组件之间传递函数,以实现更高级的交互和数据处理。这可以通过props来实现。您可以将一个函数作为prop传递给子组件,然后子组件可以调用这个函数来触发特定的操作。
例如,假设您有一个父组件和一个子组件,父组件传递了一个函数给子组件作为prop。子组件可以在某个事件发生时调用这个函数,从而通知父组件进行某些处理。
2.1. 组件拆分第一版
2.1.1. App.vue代码
<template><div id="root"><div class="todo-container"><div class="todo-wrap"><MyHeader :addTodo="addTodo"/><MyList :todos="todos" :checkTodo="checkTodo" :deleteTodo="deleteTodo"/><MyFooter :todos="todos" :checkAllTodo="checkAllTodo" :clearAllTodo="clearAllTodo"/></div></div></div>
</template><script>import MyHeader from './components/MyHeader'import MyList from './components/MyList'import MyFooter from './components/MyFooter.vue'export default {name:'App',components:{MyHeader,MyList,MyFooter},data() {return {//由于todos是MyHeader组件和MyFooter组件都在使用,所以放在App中(状态提升)todos:[{id:'001',title:'抽烟',done:true},{id:'002',title:'喝酒',done:false},{id:'003',title:'开车',done:true}]}},methods: {//添加一个todoaddTodo(todoObj){this.todos.unshift(todoObj)},//勾选or取消勾选一个todocheckTodo(id){this.todos.forEach((todo)=>{if(todo.id === id) todo.done = !todo.done})},//删除一个tododeleteTodo(id){this.todos = this.todos.filter( todo => todo.id !== id )},//全选or取消全选checkAllTodo(done){this.todos.forEach((todo)=>{todo.done = done})},//清除所有已经完成的todoclearAllTodo(){this.todos = this.todos.filter((todo)=>{return !todo.done})}}}
</script><style>/*base*/body {background: #fff;}.btn {display: inline-block;padding: 4px 12px;margin-bottom: 0;font-size: 14px;line-height: 20px;text-align: center;vertical-align: middle;cursor: pointer;box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);border-radius: 4px;}.btn-danger {color: #fff;background-color: #da4f49;border: 1px solid #bd362f;}.btn-danger:hover {color: #fff;background-color: #bd362f;}.btn:focus {outline: none;}.todo-container {width: 600px;margin: 0 auto;}.todo-container .todo-wrap {padding: 10px;border: 1px solid #ddd;border-radius: 5px;}
</style>
2.1.2. 各个子组件代码
2.1.2.1. MyHeader.vue 代码
<template><div class="todo-header"><input type="text" placeholder="请输入你的任务名称,按回车键确认" v-model="title" @keyup.enter="add"/></div>
</template><script>import {nanoid} from 'nanoid'export default {name:'MyHeader',//接收从App传递过来的addTodoprops:['addTodo'],data() {return {//收集用户输入的titletitle:''}},methods: {add(){//校验数据if(!this.title.trim()) return alert('输入不能为空')//将用户的输入包装成一个todo对象const todoObj = {id:nanoid(),title:this.title,done:false}//通知App组件去添加一个todo对象this.addTodo(todoObj)//清空输入this.title = ''}},}
</script><style scoped>/*header*/.todo-header input {width: 560px;height: 28px;font-size: 14px;border: 1px solid #ccc;border-radius: 4px;padding: 4px 7px;}.todo-header input:focus {outline: none;border-color: rgba(82, 168, 236, 0.8);box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);}
</style>
2.1.2.2. MyList.vue 代码
<template><ul class="todo-main"><MyItemv-for="todoObj in todos":key="todoObj.id":todo="todoObj":checkTodo="checkTodo":deleteTodo="deleteTodo"/></ul>
</template><script>import MyItem from './MyItem'export default {name:'MyList',components:{MyItem},//声明接收App传递过来的数据,其中todos是自己用的,checkTodo和deleteTodo是给子组件MyItem用的props:['todos','checkTodo','deleteTodo']}
</script><style scoped>/*main*/.todo-main {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;}
</style>
2.1.2.3. MyItem.vue 代码
<template><li><label><input type="checkbox" :checked="todo.done" @change="handleCheck(todo.id)"/><!-- 如下代码也能实现功能,但是不太推荐,因为有点违反原则,因为修改了props --><!-- <input type="checkbox" v-model="todo.done"/> --><span>{{todo.title}}</span></label><button class="btn btn-danger" @click="handleDelete(todo.id)">删除</button></li>
</template><script>export default {name:'MyItem',//声明接收todo、checkTodo、deleteTodoprops:['todo','checkTodo','deleteTodo'],methods: {//勾选or取消勾选handleCheck(id){//通知App组件将对应的todo对象的done值取反this.checkTodo(id)},//删除handleDelete(id){if(confirm('确定删除吗?')){//通知App组件将对应的todo对象删除this.deleteTodo(id)}}},}
</script><style scoped>/*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;}li:hover{background-color: #ddd;}li:hover button{display: block;}
</style>
2.1.2.4. MyFooter.vue 代码
<template><div class="todo-footer" v-show="total"><label><!-- <input type="checkbox" :checked="isAll" @change="checkAll"/> --><input type="checkbox" v-model="isAll"/></label><span><span>已完成{{doneTotal}}</span> / 全部{{total}}</span><button class="btn btn-danger" @click="clearAll">清除已完成任务</button></div>
</template><script>export default {name:'MyFooter',props:['todos','checkAllTodo','clearAllTodo'],computed: {//总数total(){return this.todos.length},//已完成数doneTotal(){//此处使用reduce方法做条件统计/* const x = this.todos.reduce((pre,current)=>{console.log('@',pre,current)return pre + (current.done ? 1 : 0)},0) *///简写return this.todos.reduce((pre,todo)=> pre + (todo.done ? 1 : 0) ,0)},//控制全选框isAll:{//全选框是否勾选get(){return this.doneTotal === this.total && this.total > 0},//isAll被修改时set被调用set(value){this.checkAllTodo(value)}}},methods: {/* checkAll(e){this.checkAllTodo(e.target.checked)} *///清空所有已完成clearAll(){this.clearAllTodo()}},}
</script><style scoped>/*footer*/.todo-footer {height: 40px;line-height: 40px;padding-left: 6px;margin-top: 5px;}.todo-footer label {display: inline-block;margin-right: 20px;cursor: pointer;}.todo-footer label input {position: relative;top: -1px;vertical-align: middle;margin-right: 5px;}.todo-footer button {float: right;margin-top: 5px;}
</style>
2.1.3. 代码层次和流程流转示意图
2.1.3.1. 控件层次示意图
2.1.3.2. 控件初始化数据与事件绑定示意图
2.2. 事件总线版本
事件总线是一种模式,用于在Vue应用程序中实现组件之间的通信,即使这些组件没有直接的父子关系。您可以创建一个Vue实例,作为事件总线,用于触发和监听事件。
通过事件总线,您可以在任何组件中触发和监听事件,实现了组件之间的解耦和通信。请注意,事件总线在大型应用中可能会导致事件管理变得复杂,所以需要慎重使用。
2.2.1. main.js 代码
//引入Vue
import Vue from 'vue'
//引入App
import App from './App.vue'//关闭Vue的生产提示
Vue.config.productionTip = false//创建vm
new Vue({el:'#app',render: h => h(App),beforeCreate() {Vue.prototype.$bus = this},
})
2.2.2. App.vue 代码
<template><div id="root"><div class="todo-container"><div class="todo-wrap"><MyHeader @addTodo="addTodo"/><MyList :todos="todos"/><MyFooter :todos="todos" @checkAllTodo="checkAllTodo" @clearAllTodo="clearAllTodo"/></div></div></div>
</template><script>import MyHeader from './components/MyHeader'import MyList from './components/MyList'import MyFooter from './components/MyFooter.vue'export default {name:'App',components:{MyHeader,MyList,MyFooter},data() {return {//由于todos是MyHeader组件和MyFooter组件都在使用,所以放在App中(状态提升)todos:JSON.parse(localStorage.getItem('todos')) || []}},methods: {//添加一个todoaddTodo(todoObj){this.todos.unshift(todoObj)},//勾选or取消勾选一个todocheckTodo(id){this.todos.forEach((todo)=>{if(todo.id === id) todo.done = !todo.done})},//删除一个tododeleteTodo(id){this.todos = this.todos.filter( todo => todo.id !== id )},//全选or取消全选checkAllTodo(done){this.todos.forEach((todo)=>{todo.done = done})},//清除所有已经完成的todoclearAllTodo(){this.todos = this.todos.filter((todo)=>{return !todo.done})}},watch: {todos:{deep:true,handler(value){localStorage.setItem('todos',JSON.stringify(value))}}},mounted() {this.$bus.$on('checkTodo',this.checkTodo)this.$bus.$on('deleteTodo',this.deleteTodo)},beforeDestroy() {this.$bus.$off('checkTodo')this.$bus.$off('deleteTodo')},}
</script><style>/*base*/body {background: #fff;}.btn {display: inline-block;padding: 4px 12px;margin-bottom: 0;font-size: 14px;line-height: 20px;text-align: center;vertical-align: middle;cursor: pointer;box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);border-radius: 4px;}.btn-danger {color: #fff;background-color: #da4f49;border: 1px solid #bd362f;}.btn-danger:hover {color: #fff;background-color: #bd362f;}.btn:focus {outline: none;}.todo-container {width: 600px;margin: 0 auto;}.todo-container .todo-wrap {padding: 10px;border: 1px solid #ddd;border-radius: 5px;}
</style>
2.2.3. 各个子组件代码
2.2.3.1. MyHeader.vue 代码
<template><div class="todo-header"><input type="text" placeholder="请输入你的任务名称,按回车键确认" v-model="title" @keyup.enter="add"/></div>
</template><script>import {nanoid} from 'nanoid'export default {name:'MyHeader',data() {return {//收集用户输入的titletitle:''}},methods: {add(){//校验数据if(!this.title.trim()) return alert('输入不能为空')//将用户的输入包装成一个todo对象const todoObj = {id:nanoid(),title:this.title,done:false}//通知App组件去添加一个todo对象this.$emit('addTodo',todoObj,1,2,3)//清空输入this.title = ''}},}
</script><style scoped>/*header*/.todo-header input {width: 560px;height: 28px;font-size: 14px;border: 1px solid #ccc;border-radius: 4px;padding: 4px 7px;}.todo-header input:focus {outline: none;border-color: rgba(82, 168, 236, 0.8);box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);}
</style>
2.2.3.2. MyList代码
<template><ul class="todo-main"><MyItemv-for="todoObj in todos":key="todoObj.id":todo="todoObj"/></ul>
</template><script>import MyItem from './MyItem'export default {name:'MyList',components:{MyItem},//声明接收App传递过来的数据props:['todos']}
</script><style scoped>/*main*/.todo-main {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;}
</style>
2.2.3.3. MyItem.vue 代码
<template><li><label><input type="checkbox" :checked="todo.done" @change="handleCheck(todo.id)"/><!-- 如下代码也能实现功能,但是不太推荐,因为有点违反原则,因为修改了props --><!-- <input type="checkbox" v-model="todo.done"/> --><span>{{todo.title}}</span></label><button class="btn btn-danger" @click="handleDelete(todo.id)">删除</button></li>
</template><script>export default {name:'MyItem',//声明接收todoprops:['todo'],methods: {//勾选or取消勾选handleCheck(id){//通知App组件将对应的todo对象的done值取反// this.checkTodo(id)this.$bus.$emit('checkTodo',id)},//删除handleDelete(id){if(confirm('确定删除吗?')){//通知App组件将对应的todo对象删除// this.deleteTodo(id)this.$bus.$emit('deleteTodo',id)}}},}
</script><style scoped>/*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;}li:hover{background-color: #ddd;}li:hover button{display: block;}
</style>
2.2.3.4. MyFooter.vue 代码
<template><div class="todo-footer" v-show="total"><label><!-- <input type="checkbox" :checked="isAll" @change="checkAll"/> --><input type="checkbox" v-model="isAll"/></label><span><span>已完成{{doneTotal}}</span> / 全部{{total}}</span><button class="btn btn-danger" @click="clearAll">清除已完成任务</button></div>
</template><script>export default {name:'MyFooter',props:['todos'],computed: {//总数total(){return this.todos.length},//已完成数doneTotal(){//此处使用reduce方法做条件统计/* const x = this.todos.reduce((pre,current)=>{console.log('@',pre,current)return pre + (current.done ? 1 : 0)},0) *///简写return this.todos.reduce((pre,todo)=> pre + (todo.done ? 1 : 0) ,0)},//控制全选框isAll:{//全选框是否勾选get(){return this.doneTotal === this.total && this.total > 0},//isAll被修改时set被调用set(value){// this.checkAllTodo(value)this.$emit('checkAllTodo',value)}}},methods: {/* checkAll(e){this.checkAllTodo(e.target.checked)} *///清空所有已完成clearAll(){// this.clearAllTodo()this.$emit('clearAllTodo')}},}
</script><style scoped>/*footer*/.todo-footer {height: 40px;line-height: 40px;padding-left: 6px;margin-top: 5px;}.todo-footer label {display: inline-block;margin-right: 20px;cursor: pointer;}.todo-footer label input {position: relative;top: -1px;vertical-align: middle;margin-right: 5px;}.todo-footer button {float: right;margin-top: 5px;}
</style>
2.2.4. 代码流程流转示意图
2.3. 发布订阅模式版本
pubsub-js
是一个JavaScript库,提供了发布-订阅模式的实现,用于在应用程序中实现组件之间的解耦通信。它允许不同的组件在不直接相互关联的情况下进行通信。这个库提供了一个简单的方式来发送消息并订阅事件,从而使应用程序的代码更加模块化和可维护。
导入pubsub-js
2.3.1. main.js 代码
//引入Vue
import Vue from 'vue'
//引入App
import App from './App.vue'
//关闭Vue的生产提示
Vue.config.productionTip = false//创建vm
new Vue({el:'#app',render: h => h(App),
})
2.3.2. App.vue 代码
<template><div id="root"><div class="todo-container"><div class="todo-wrap"><MyHeader @addTodo="addTodo"/><MyList :todos="todos"/><MyFooter :todos="todos" @checkAllTodo="checkAllTodo" @clearAllTodo="clearAllTodo"/></div></div></div>
</template><script>import pubsub from 'pubsub-js'import MyHeader from './components/MyHeader'import MyList from './components/MyList'import MyFooter from './components/MyFooter'export default {name:'App',components:{MyHeader,MyList,MyFooter},data() {return {//由于todos是MyHeader组件和MyFooter组件都在使用,所以放在App中(状态提升)todos:JSON.parse(localStorage.getItem('todos')) || []}},methods: {//添加一个todoaddTodo(todoObj){this.todos.unshift(todoObj)},//勾选or取消勾选一个todocheckTodo(id){this.todos.forEach((todo)=>{if(todo.id === id) todo.done = !todo.done})},//删除一个tododeleteTodo(_,id){this.todos = this.todos.filter( todo => todo.id !== id )},//全选or取消全选checkAllTodo(done){this.todos.forEach((todo)=>{todo.done = done})},//清除所有已经完成的todoclearAllTodo(){this.todos = this.todos.filter((todo)=>{return !todo.done})}},watch: {todos:{deep:true,handler(value){localStorage.setItem('todos',JSON.stringify(value))}}},mounted() {this.addTodoPubId = pubsub.subscribe('addTodo',this.addTodo)this.checkTodoPubId = pubsub.subscribe('checkTodo',this.checkTodo)this.deleteTodoPubId = pubsub.subscribe('deleteTodo',this.deleteTodo)this.checkAllTodoPubId = pubsub.subscribe('checkTodo',this.checkAllTodo)this.clearAllTodoPubId = pubsub.subscribe('deleteTodo',this.clearAllTodo)},beforeDestroy() {pubsub.unsubscribe(this.addTodoPubId)pubsub.unsubscribe(this.checkTodoPubId)pubsub.unsubscribe(this.deleteTodoPubId)pubsub.unsubscribe(this.checkAllTodoPubId)pubsub.unsubscribe(this.clearAllTodoPubId)},}
</script><style>/*base*/body {background: #fff;}.btn {display: inline-block;padding: 4px 12px;margin-bottom: 0;font-size: 14px;line-height: 20px;text-align: center;vertical-align: middle;cursor: pointer;box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);border-radius: 4px;}.btn-danger {color: #fff;background-color: #da4f49;border: 1px solid #bd362f;}.btn-danger:hover {color: #fff;background-color: #bd362f;}.btn:focus {outline: none;}.todo-container {width: 600px;margin: 0 auto;}.todo-container .todo-wrap {padding: 10px;border: 1px solid #ddd;border-radius: 5px;}
</style>
2.3.3. 各个子组件代码
2.3.3.1. MyHeader.vue 代码
<template><div class="todo-header"><input type="text" placeholder="请输入你的任务名称,按回车键确认" v-model="title" @keyup.enter="add"/></div>
</template><script>import pubsub from 'pubsub-js'import {nanoid} from 'nanoid'export default {name:'MyHeader',data() {return {//收集用户输入的titletitle:''}},methods: {add(){//校验数据if(!this.title.trim()) return alert('输入不能为空')//将用户的输入包装成一个todo对象const todoObj = {id:nanoid(),title:this.title,done:false}//通知App组件去添加一个todo对象//this.$emit('addTodo',todoObj,1,2,3)pubsub.publish('addTodo',todoObj)//清空输入this.title = ''}},}
</script><style scoped>/*header*/.todo-header input {width: 560px;height: 28px;font-size: 14px;border: 1px solid #ccc;border-radius: 4px;padding: 4px 7px;}.todo-header input:focus {outline: none;border-color: rgba(82, 168, 236, 0.8);box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);}
</style>
2.3.3.2. MyList.vue 代码
<template><ul class="todo-main"><MyItemv-for="todoObj in todos":key="todoObj.id":todo="todoObj"/></ul>
</template><script>import MyItem from './MyItem'export default {name:'MyList',components:{MyItem},//声明接收App传递过来的数据props:['todos']}
</script><style scoped>/*main*/.todo-main {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;}
</style>
2.3.3.3. MyItem.vue 代码
<template><li><label><input type="checkbox" :checked="todo.done" @change="handleCheck(todo.id)"/><!-- 如下代码也能实现功能,但是不太推荐,因为有点违反原则,因为修改了props --><!-- <input type="checkbox" v-model="todo.done"/> --><span>{{todo.title}}</span></label><button class="btn btn-danger" @click="handleDelete(todo.id)">删除</button></li>
</template><script>import pubsub from 'pubsub-js'export default {name:'MyItem',//声明接收todoprops:['todo'],methods: {//勾选or取消勾选handleCheck(id){//通知App组件将对应的todo对象的done值取反// this.checkTodo(id)//this.$bus.$emit('checkTodo',id)pubsub.publish('checkTodo',id)},//删除handleDelete(id){if(confirm('确定删除吗?')){//通知App组件将对应的todo对象删除// this.deleteTodo(id)// this.$bus.$emit('deleteTodo',id)pubsub.publish('deleteTodo',id)}}},}
</script><style scoped>/*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;}li:hover{background-color: #ddd;}li:hover button{display: block;}
</style>
2.3.3.4. MyFooter.vue 代码
<template><div class="todo-footer" v-show="total"><label><!-- <input type="checkbox" :checked="isAll" @change="checkAll"/> --><input type="checkbox" v-model="isAll"/></label><span><span>已完成{{doneTotal}}</span> / 全部{{total}}</span><button class="btn btn-danger" @click="clearAll">清除已完成任务</button></div>
</template><script>import pubsub from 'pubsub-js'export default {name:'MyFooter',props:['todos'],computed: {//总数total(){return this.todos.length},//已完成数doneTotal(){//此处使用reduce方法做条件统计/* const x = this.todos.reduce((pre,current)=>{console.log('@',pre,current)return pre + (current.done ? 1 : 0)},0) *///简写return this.todos.reduce((pre,todo)=> pre + (todo.done ? 1 : 0) ,0)},//控制全选框isAll:{//全选框是否勾选get(){return this.doneTotal === this.total && this.total > 0},//isAll被修改时set被调用set(value){// this.checkAllTodo(value)// this.$emit('checkAllTodo',value)pubsub.publish('checkAllTodo',id)}}},methods: {/* checkAll(e){this.checkAllTodo(e.target.checked)} *///清空所有已完成clearAll(){// this.clearAllTodo()// this.$emit('clearAllTodo')pubsub.publish('clearAllTodo')}},}
</script><style scoped>/*footer*/.todo-footer {height: 40px;line-height: 40px;padding-left: 6px;margin-top: 5px;}.todo-footer label {display: inline-block;margin-right: 20px;cursor: pointer;}.todo-footer label input {position: relative;top: -1px;vertical-align: middle;margin-right: 5px;}.todo-footer button {float: right;margin-top: 5px;}
</style>
2.3.4. 代码流程流转示意图
3. 发布订阅模式和事件总线之间的区别 优劣
发布-订阅模式和事件总线都是用于实现组件之间的通信的模式,但它们在一些方面有所不同。下面是它们之间的区别以及各自的优劣势:
发布-订阅模式:
定义:
发布-订阅模式是一种模式,其中有一个调度中心(通常称为“发布者”),负责管理订阅者(也称为“订阅者”)。订阅者可以订阅感兴趣的事件,而发布者发布这些事件。当事件被发布时,订阅者会收到通知并执行相关操作。
优势:
- 解耦性强:发布者和订阅者之间没有直接关联,从而实现了松耦合。
- 可以有多个订阅者:多个订阅者可以同时订阅同一个事件,实现了多对多的通信。
- 灵活性:允许动态添加和移除订阅者,以及在不影响其他部分的情况下修改事件的处理逻辑。
劣势:
- 调试和追踪:当订阅者数量增加时,跟踪事件的流向和处理变得更加复杂。
- 维护困难:如果不恰当地使用,可能导致事件的处理分散和难以维护。
事件总线:
定义:
事件总线是一个中央的通信渠道,用于在不同组件之间传递消息。在Vue中,通常是通过创建一个Vue实例作为事件总线,允许组件通过该实例来发布和订阅事件。
优势:
- 简单易用:Vue的事件总线机制非常简单,适用于小规模的组件通信。
- Vue内置:作为Vue框架的一部分,事件总线是原生支持的,无需额外的库。
- 组件间通信:可以方便地在任何两个组件之间进行通信,而不考虑它们的层级关系。
劣势:
- 限制:事件总线适用于小型应用,但对于大型应用,可能会导致事件处理逻辑分散且难以追踪。
- 耦合性:事件总线可能导致组件之间的耦合性增加,因为事件可以被任何组件订阅,难以管理。
结论:
发布-订阅模式和事件总线都有各自的优劣势,最适合的解决方案取决于您的应用程序的规模和需求。对于小型应用,事件总线可能更加简便,但对于大型应用,您可能需要考虑更结构化的通信方案,如Vuex状态管理、单向数据流等,以避免通信变得混乱和难以维护。在使用这些模式时,始终要考虑代码的清晰性、可维护性和可扩展性。