一個案例:Vue2組件化開發組件從入門到入土

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狀態管理、單向數據流等,以避免通信變得混亂和難以維護。在使用這些模式時,始終要考慮代碼的清晰性、可維護性和可擴展性。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/42012.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/42012.shtml
英文地址,請注明出處:http://en.pswp.cn/news/42012.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

open cv學習 (五) 圖像的閾值處理

圖像的閾值處理 demo1 # 二值化處理黑白漸變圖 import cv2 img cv2.imread("./img.png", 0) # 二值化處理 t1, dst cv2.threshold(img, 127, 255, cv2.THRESH_BINARY) cv2.imshow("img", img) cv2.imshow("dst", dst) cv2.waitKey() cv2.des…

Golang使用MinIO

最近在使用Golang做了一個網盤項目&#xff08;學習&#xff09;&#xff0c;文件存儲一直保存在本地&#xff08;各廠商提供的oss貴&#xff09;&#xff0c;所以就在思考怎么來處理這些文件&#xff0c;類似的方案很對hdfs、fastdfs&#xff0c;但這其中MinIO是最近幾年比較火…

生信豆芽菜-差異基因富集分析的圈圖

網址&#xff1a;http://www.sxdyc.com/visualsEnrichCirplot 1、數據準備 準備一個基因集的文件 2、選擇富集分析的數據庫&#xff0c;同時輸入展示top幾的條目&#xff0c;選擇顏色&#xff0c;如果是GO的話選擇三個顏色&#xff0c;如果是KEGG選擇一個&#xff0c;如果是G…

神經網絡論文研讀-多模態方向-綜述研讀(上)

翻譯以機翻為主 原文目錄 前言 圖1&#xff1a;LMU印章&#xff08;左&#xff09;風格轉移到梵高的向日葵繪畫&#xff08;中&#xff09;并與提示混合 - 梵高&#xff0c;向日葵 -通過CLIPVGAN&#xff08;右&#xff09;。在過去的幾年中&#xff0c;自然語言處理&#xff…

微信小程序實現拖拽的小球

目錄 前言 js 獲取微信小程序中獲取系統信息 觸摸移動事件的處理函數 觸摸結束事件的處理函數 用于監聽頁面滾動事件 全局參數 html CSS 前言 小程序開發提供了豐富的API和事件處理函數&#xff0c;使得開發者可以方便地實現各種交互功能。其中&#xff0c;拖拽功能…

無涯教程-Perl - tell函數

描述 此函數返回指定FILEHANDLE中讀取指針的當前位置(以字節為單位)。如果省略FILEHANDLE,則它將返回上次訪問的文件中的位置。 語法 以下是此函數的簡單語法- tell FILEHANDLEtell返回值 此函數以字節為單位返回當前文件位置。 例 以下是顯示其基本用法的示例代碼,要檢…

leetcode473. 火柴拼正方形(回溯算法-java)

火柴拼正方形 leetcode473 火柴拼正方形題目描述回溯算法 上期經典算法 leetcode473 火柴拼正方形 難度 - 中等 原題鏈接 - leetcode473 火柴拼正方形 題目描述 你將得到一個整數數組 matchsticks &#xff0c;其中 matchsticks[i] 是第 i 個火柴棒的長度。你要用 所有的火柴棍…

BC119 小樂樂與字符串

描述 在慶祝祖國母親70華誕之際&#xff0c;老師給小樂樂出了一個問題。大家都知道China的英文縮寫是CHN&#xff0c;那么給你一個字符串s&#xff0c;你需要做的是統計s中子序列“CHN”的個數。子序列的定義&#xff1a;存在任意下標a < b < c&#xff0c;那么“s[a]s[b…

微服務—Eureka注冊中心

eureka相當于是一個公司的管理人事HR,各部門之間如果有合作時&#xff0c;由HR進行人員的分配以及調度&#xff0c;具體選哪個人&#xff0c;全憑HR的心情&#xff0c;如果你這個部門存在沒有意義&#xff0c;直接把你這個部門撤銷&#xff0c;全體人員裁掉&#xff0c;所以不想…

計算機網絡筆記

TCP有連接可靠服務 TCP特點&#xff1a; 1.TCP是面向連接的傳輸層協議&#xff1b; 2.每條TCP連接只能有兩個端點&#xff0c;每條TCP連接是一對一的&#xff1b; 3.TCP提供可靠交付&#xff0c;保證傳送數據無差錯&#xff0c;不丟失&#xff0c;不重復且有序&#xff1b; 4.…

Android Studio瀑布流實現

效果&#xff1a; ImageDetail class package com.example.waterfallflow; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.widget.ImageView;public class ImageDetail extends Activity{Overrideprotected void …

DNNGP、DeepGS 和 DLGWAS模型構成對比

一、DNNGP DNNGP 是基于深度卷積神經網絡&#xff0c;這個結構包括一個輸入層&#xff0c;三個卷積層&#xff0c;一個批標準化層&#xff0c;兩個dropout層&#xff0c;一個平坦化層&#xff0c;一個 dense層。 dropout層&#xff1a;在神經網絡中,dropout層是一個非常有效的正…

信息與通信工程面試準備——數學知識|正態分布|中心極限定理

目錄 正態分布 正態分布的參數 正態分布的第一個參數是均值 正態分布的第二個參數是標準差SD 所有正態分布的共同特征 標準正態分布&#xff1a;正態分布的特例 中心極限定理 理解定義 示例# 1 示例# 2 知道樣本均值總是正態分布的實際含義是什么&#xff1f; 正態分…

Scala 如何調試隱式轉換--隱式轉換代碼的顯示展示

方法1 在需要隱式轉換的地方&#xff0c;把需要的參數顯示的寫出。 略方法2&#xff0c;查看編譯代碼 在terminal中 利用 scalac -Xprint:typer xxx.scala方法打印添加了隱式值的代碼示例。 對于復雜的工程來說&#xff0c;直接跑到terminal執行 scalac -Xprint:typer xxx.…

JVM——類文件結構

文章目錄 一 概述二 Class 文件結構總結2.1 魔數2.2 Class 文件版本2.3 常量池2.4 訪問標志2.5 當前類索引,父類索引與接口索引集合2.6 字段表集合2.7 方法表集合2.8 屬性表集合 一 概述 在 Java 中&#xff0c;JVM 可以理解的代碼就叫做字節碼&#xff08;即擴展名為 .class …

winform 封裝unity web player 用戶控件

環境&#xff1a; VS2015Unity 5.3.6f1 (64-bit) 目的&#xff1a; Unity官方提供的UnityWebPlayer控件在嵌入Winform時要求讀取的.unity3d文件路徑&#xff08;Src&#xff09;必須是絕對路徑&#xff0c;如果移動代碼到另一臺電腦&#xff0c;需要重新修改src。于是考慮使…

elementUI 的上傳組件<el-upload>,自定義上傳按鈕樣式

方法一&#xff1a; 原理&#xff1a;調用<el-upload>組件的方法喚起選擇文件事件 效果&#xff1a; 頁面代碼&#xff1a; 1、選擇圖片按鈕 <div class"flex_row_spacebetween btn" click"chooseImg"><span class"el-icon-plus ic…

matlab機器人工具箱基礎使用

資料&#xff1a;https://blog.csdn.net/huangjunsheng123/article/details/110630665 用vscode直接看工具箱api代碼比較方便&#xff0c;代碼說明很多 一、模型設置 1、基礎效果 %采用機器人工具箱進行正逆運動學驗證 a[0,-0.3,-0.3,0,0,0];%DH參數 d[0.05,0,0,0.06,0.05,…

教育行業軟文怎么寫,媒介盒子無償分享

隨著產業升級和技術變革、信息的智能化、數字化發展&#xff0c;也為教育行業帶來了新的增長點&#xff0c;在線教育課程類型豐富多元&#xff0c;新課程不斷涌現。在激烈的市場競爭環境下&#xff0c;教育機構如何根據市場實行差異化戰略并加強自身品牌建成為挑戰。 如今&…

微服務-Ribbon(負載均衡)

負載均衡的面對多個相同的服務的時候&#xff0c;我們選擇一定的策略去選擇一個服務進行 負載均衡流程 Ribbon結構組成 負載均衡策略 RoundRobinRule&#xff1a;簡單的輪詢服務列表來選擇服務器AvailabilityFilteringRule 對兩種情況服務器進行忽略&#xff1a; 1.在默認情…