Vue是一个轻量级的前端框架,它使用了多种设计模式来实现高效、灵活和可维护的代码。在这篇博客中,我将介绍Vue中常用的二种设计模式,分别是:
- 观察者模式:这是一种行为型设计模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象,当主题对象的状态发生变化时,就会通知所有观察者对象,使它们能够自动更新自己。
- 发布订阅模式:这是一种行为型设计模式,它定义了一种对象间的一对多的依赖关系,让多个订阅者对象同时订阅某一个主题对象,当主题对象发出一个通知时,就会调用所有订阅者对象的方法。
下面我将分别对这二种设计模式进行详细的介绍,并对比分析它们的优缺点以及使用案例。
观察者模式
观察者模式是Vue中最核心的设计模式之一,它实现了Vue的响应式数据绑定。在Vue中,每个数据都被封装成一个Observer对象,它可以监听自身属性的变化,并通知所有订阅了该属性的Watcher对象。Watcher对象是一个观察者,它可以订阅多个Observer对象,并在收到通知时执行相应的回调函数。Dep对象是一个依赖收集器,它可以管理多个Watcher对象,并在Observer对象发生变化时通知所有Watcher对象。
观察者模式的优点有:
- 实现了数据和视图的自动同步,无需手动操作DOM。
- 实现了数据和视图之间的解耦,提高了代码的可复用性和可测试性。
- 实现了数据变化的精确追踪和通知,提高了性能和效率。
观察者模式的缺点有:
- 增加了内存消耗和垃圾回收压力,因为需要创建大量的Observer、Watcher和Dep对象。
- 无法检测到数组和对象内部属性的变化,需要使用特殊的方法来触发更新。
- 无法检测到原始类型数据(如字符串、数字、布尔值)的变化,需要使用计算属性或者watch选项来监听。
观察者模式在Vue中的使用案例有:
-
在data选项中定义响应式数据,在template中使用插值表达式或者指令来绑定数据,当数据发生变化时,视图会自动更新。例如:
-
在computed选项中定义计算属性,它是一个基于响应式数据的派生数据,当响应式数据发生变化时,计算属性会自动重新计算并更新视图。例如:
- 在watch选项中定义监听器,它是一个基于响应式数据的回调函数,当响应式数据发生变化时,监听器会自动执行并触发一些副作用。例如:
发布订阅模式
发布订阅模式是Vue中常用的设计模式之一,它实现了Vue的事件系统。在Vue中,每个组件都是一个EventEmitter对象,它可以发布和订阅事件,并在事件触发时执行相应的回调函数。EventEmitter对象内部维护了一个事件列表,每个事件都对应一个订阅者数组,当发布一个事件时,就会遍历该数组并调用所有订阅者的方法。
发布订阅模式的优点有:
- 实现了组件间的通信和协作,无需依赖父子关系或者全局状态。
- 实现了组件间的解耦,提高了代码的可扩展性和可维护性。
- 实现了事件的动态注册和注销,提高了代码的灵活性和可控性。
发布订阅模式的缺点有:
- 增加了内存消耗和垃圾回收压力,因为需要创建大量的事件和订阅者对象。
- 增加了代码的复杂度和难以理解性,因为事件的流向和触发时机不容易追踪和调试。
- 增加了代码的耦合度和不稳定性,因为事件的命名和参数需要保持一致,否则会导致错误或者丢失。
发布订阅模式在Vue中的使用案例有:
- 在组件内部使用on方法来发布和订阅事件,实现组件自身的逻辑和状态管理。例如:
- 在组件间使用on方法来发布和订阅事件,实现组件间的通信和协作。例如:
- 在组件和非组件之间使用on方法来发布和订阅事件,实现组件和非组件之间的通信和协作。例如:
<template>
<div id="app">
<child></child>
</div>
</template>
<script>
import Child from "./Child.vue";
export default {
components: {
Child,
},
};
</script>
<template>
<div>
<p>子组件</p>
<button @click="sendToParent">向父组件发送消息</button>
</div>
</template>
<script>
export default {
methods: {
sendToParent() {
// 发布message事件,传递一个参数
this.$emit("message", "Hello, I am child.");
},
},
};
</script>
// 非组件的js文件
import Vue from "vue";
// 创建一个Vue实例作为事件总线
const bus = new Vue();
// 订阅message事件,接收一个参数
bus.$on("message", (data) => {
console.log(data);
});