在Vue的开发中,
Vue2 中会用侦听器watch来监听数据的变化。
Vue3 中使用 proxy 替代了defineProperty用来监听整个对象 ,性能大大提高。
那如果想监听DOM的变化呢,该怎么做呢?

JavaScript 提供了多种 API 来操作 DOM 结构。而在操作 DOM 时,经常需要监测 DOM 的变化,此时,MutationObserver 就显得非常有用。
一、MutationObserver 简介
MutationObserver 是 HTML5 引入的一种用于监听 DOM 树变化的接口。它可以在 DOM 树发生以下变化时执行回调函数:
- 元素的子树发生变化(子节点的添加、删除或重排序)。
- 元素的属性发生变化。
- 元素的文本内容发生变化。
与传统的 DOM 事件(如 DOMSubtreeModified、DOMNodeInserted、DOMNodeRemoved 等)相比,MutationObserver 提供了更高效和更灵活的 API。
原理:
MutationObserver 通过异步方式监测 DOM 变化,这意味着当 DOM 变化发生时,MutationObserver 不会立即执行回调函数,而是将这些变化存入一个队列中,并在本轮 JavaScript 执行完之后,才批量处理这些变化。这种异步批量处理的机制,使得 MutationObserver 更加高效。
二、MutationObserver 基础用法
创建一个 MutationObserver 实例,传入一个回调函数。
使用 observe 方法开始监听目标节点及其相关的变化。
当不再需要监听时,使用 disconnect 方法停止观察。停止MutationObserver对象的观察,且清空所有的MutationRecord对象。
observe 配置项:
接受两个参数:目标节点和一个配置对象。配置对象用于指定要观察哪些类型的变化。常用配置项包括:
|-----------------------|--------------------------------------------|-------|
| 属性 | 说明 | 默认值 |
| subtree | 当设置为 true 时,监视目标节点及其所有后代节点的变化 | false |
| childList | 当目标节点(如果 subtree 为 true,则包含子孙节点)添加或删除时触发回调 | false |
| attributes | 当元素的属性变化时触发回调 | false |
| attributeFilter | 要监视的特定属性名称的数组。如果未包含此属性,则对所有属性的更改都会触发变动通知 | 无默认值 |
| characterData | 设为 true 以监视指定目标节点或子节点树中节点所包含的字符数据的变化 | false |
| attributeOldValue | 当属性变化时,记录变化前的属性值 | false |
| characterDataOldValue | 当文本节点变化时,记录变化前的文本内容 | false |
代码实现:
// 1. 创建一个 MutationObserver 实例,并传入回调函数
const observer = new MutationObserver((mutationsList, observer) => {
mutationsList.forEach(mutation => {
// 处理逻辑
if (mutation.type === 'childList') {
console.log('已添加或删除子节点。');
} else if (mutation.type === 'attributes') {
console.log('修改了 ' + mutation.attributeName + ' 属性');
}
}
});
// 2. 开始监听目标节点
const elementNode = document.querySelector('#myDiv');
const config = { attributes: true, childList: true, subtree: true };
observer.observe(elementNode, config);
// 3. 停止监听
// observer.disconnect();
三、MutationObserver 使用场景
1:监控动态内容加载
页面数据通过axios接口请求动态加载到页面上的。可以使用 MutationObserver 监控动态内容的加载,在加载完成后进行一些操作(如:绑定事件、修改样式等)
// 动态内容加载容器
const contentEle = document.querySelector('.content');
// 创建观察者实例
const observer = new MutationObserver((mutationsList) => {
for (let mutation of mutationsList) {
if (mutation.type === 'childList') {
console.log('New content has been loaded:', mutation.addedNodes);
// 对新增的节点进行一些操作
mutation.addedNodes.forEach(node => {
if (node.nodeType === Node.ELEMENT_NODE) {
// 绑定事件,或执行其他逻辑
node.addEventListener('click', () => console.log('New element clicked!'));
}
});
}
}
});
// 配置选项
const config = { childList: true, subtree: true };
// 开始监控
observer.observe(contentEle, config);
2:监控属性变化
假如需要页面的某个元素 data-v-status 属性发生变化时做出响应处理。MutationObserver 可以轻松实现这一需求。
// 目标节点
const statusEle = document.querySelector('#status');
// 创建观察者实例
const observer = new MutationObserver((mutationsList) => {
for (let mutation of mutationsList) {
if (mutation.type === 'attributes' && mutation.attributeName === 'data-v-status') {
console.log('Status changed to:', statusEle.getAttribute('data-v-status'));
}
}
});
// 配置选项
const config = { attributes: true, attributeFilter: ['data-v-status'] };
// 开始监控
observer.observe(statusEle, config);
3:批量修改 DOM, 优化页面性能
批量向Dom节点中添加了 N多个子节点, MutationObserver 会统一处理这些 DOM 变化,有效减少了重绘和重排操作,优化页面性能。
// 目标节点
const containerEle = document.querySelector('#container');
// 创建观察者实例
const observer = new MutationObserver((mutationsList) => {
mutationsList.forEach(mutation => {
if (mutation.type === 'childList') {
// 批量处理逻辑
console.log('批量处理DOM更改');
}
});
});
// 配置选项
const config = { childList: true, subtree: true };
// 开始监控
observer.observe(containerEle, config);
// 批量修改 DOM
const fragment = document.createDocumentFragment();
for (let i = 0; i < 500; i++) {
const newDiv = document.createElement('div');
newDiv.textContent = `Item ${i}`;
fragment.appendChild(newDiv);
}
containerEle.appendChild(fragment);
4:删除恶意Javascript脚本, 防止 DOM 劫持
在一些恶意脚本或第三方插件注入的情况下,DOM 结构可能会被劫持。可以使用 MutationObserver 检测 DOM 结构的异常变化,从而做出防护措施处理。
// 目标节点
const body = document.body;
// 创建观察者实例
const observer = new MutationObserver((mutationsList) => {
for (let mutation of mutationsList) {
if (mutation.type === 'childList') {
mutation.addedNodes.forEach(node => {
if (node.nodeType === Node.ELEMENT_NODE && node.tagName === 'SCRIPT') {
console.warn('检测到潜在的恶意脚本!');
// 移除恶意脚本
node.parentNode.removeChild(node);
}
});
}
}
});
// 配置选项
const config = { childList: true, subtree: true };
// 开始监控
observer.observe(body, config);
四、结语
MutationObserver 是一个非常强大的 API,额外提供了一种高效、灵活的方式来监听和响应 DOM 变化。相比较于传统 DOM 事件监听器的诸多局限性,它通过异步、批量的方式处理 DOM 变化,极大的提高了性能和效率。在实际开发中,合理使用 MutationObserver 可以帮助我们更好地控制 DOM 操作,提高代码的健壮性和可维护性。