前言
观察者模式是一种设计模式,定义了一种一对多的依赖关系,使得当一个对象状态发生变化时,所有依赖于它的对象都会得到通知并被自动更新。
JavaScript中有多种原生接口实现了观察者模式,涵盖了从 DOM 变化、元素可见性、性能监控到消息传递等多个方面。本文将全面介绍这些原生观察者接口,给大家一个直观的感受。
1. 什么是观察者模式
用一张图直观的感受下:
可以参考下我之前写的一篇文章【源码共读】第25期 | mitt、tiny-emitter,中间写了观察者和发布订阅的区别,源码实现的方式。
下面进入正题,介绍js中原生观察者接口。
2. 原生观察者接口
2.1 EventTarget (事件监听器)
EventTarget 是一个基础接口,几乎所有的 DOM 元素都继承自它,用于实现事件监听和触发机制,是观察者模式的基础。
document.addEventListener('click', (event) => {
console.log('Document was clicked', event);
});
2.2 MutationObserver
MutationObserver 接口提供了监视对 DOM 树所做更改的能力。
用于监听DOM对象的变化,通过配置(需要观察什么变动),包括节点属性发生变化、增删改时执行相应的回调。
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
console.log(mutation);
});
});
const config = { attributes: true, childList: true, subtree: true };
observer.observe(document.body, config);
// 停止观察
// observer.disconnect();
2.3 IntersectionObserver
IntersectionObserver 接口(从属于 Intersection Observer API)提供了一种异步观察目标元素与其祖先元素或顶级文档视口(viewport)交叉状态的方法。其祖先元素或视口被称为根(root)。
当一个 IntersectionObserver 对象被创建时,其被配置为监听根中一段给定比例的可见区域。一旦 IntersectionObserver 被创建,则无法更改其配置,所以一个给定的观察者对象只能用来监听可见区域的特定变化值;然而,你可以在同一个观察者对象中配置监听多个目标元素。
实现懒加载、无限滚动等功能时非常有用。
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
console.log('Element is in viewport');
} else {
console.log('Element is out of viewport');
}
});
});
const target = document.querySelector('#myElement');
observer.observe(target);
// 停止观察
// observer.unobserve(target);
// observer.disconnect();
2.4 ResizeObserver
ResizeObserver 接口监视 Element 内容盒或边框盒或者 SVGElement 边界尺寸的变化。
用于观察元素尺寸变化。
const observer = new ResizeObserver((entries) => {
entries.forEach((entry) => {
console.log('Element size changed:', entry.contentRect);
});
});
const target = document.querySelector('#myElement');
observer.observe(target);
// 停止观察
// observer.unobserve(target);
// observer.disconnect();
2.5 PerformanceObserver
PerformanceObserver() 构造函数使用给定的观察者 callback 生成一个新的 PerformanceObserver 对象。当通过 observe() 方法注册的 条目类型 的 性能条目事件 被记录下来时,调用该观察者回调。
const observer = new PerformanceObserver((list) => {
const entries = list.getEntries();
entries.forEach((entry) => {
console.log(`Entry name: ${entry.name}`);
console.log(`Entry type: ${entry.entryType}`);
console.log(`Start time: ${entry.startTime}`);
console.log(`Duration: ${entry.duration}`);
});
});
observer.observe({ type: 'resource', buffered: true });
2.6 ReportingObserver
ReportingObserver 用于监控浏览器报告的问题,如弃用 API 使用、浏览器干预等。这有助于提前发现和解决潜在问题。
const observer = new ReportingObserver((reports) => {
reports.forEach((report) => {
console.log('Report type:', report.type);
console.log('Report URL:', report.url);
console.log('Report body:', report.body);
});
}, { buffered: true });
observer.observe();
2.7 BroadcastChannel
BroadcastChannel 接口允许在同一源下不同浏览器上下文(如不同的浏览器窗口、标签页、iframe 等)之间进行简单的消息传递。
const channel = new BroadcastChannel('my_channel');
channel.onmessage = (event) => {
console.log('Received:', event.data);
};
// 在另一个上下文中发送消息
const channel2 = new BroadcastChannel('my_channel');
channel2.postMessage('Hello from another context');
2.8 Channel Messaging API
MessageChannel 和 MessagePort 允许在同一页面内不同浏览器上下文之间传递消息。
const channel = new MessageChannel();
channel.port1.onmessage = (event) => {
console.log('Received:', event.data);
};
channel.port2.postMessage('Hello from port2');
2.9 WebSocket
WebSocket 提供了在浏览器和服务器之间创建持久连接的接口,以便双向通信。
const socket = new WebSocket('ws://localhost:8080');
socket.onopen = () => {
console.log('WebSocket connection established');
};
socket.onmessage = (event) => {
console.log('Received:', event.data);
};
socket.send('Hello, Server');
2.10 Server-Sent Events (SSE)
EventSource 接口用于从服务器接收推送事件。它是一种服务器向浏览器推送消息的单向通信。
const eventSource = new EventSource('/events');
eventSource.onmessage = (event) => {
console.log('Received:', event.data);
};
eventSource.onerror = (error) => {
console.error('EventSource failed:', error);
};
总结
最后总结一波,能够发现观察者模式其实很多地方应用。JavaScript 提供了很多的原生观察者接口,通过理解和应用这些观察者接口,在实际开发中,更加得心应手。
如有错误,请指正O^O!