在前端开发场景中,我们经常会遇到初始化函数依赖特定脚本执行完成或者特定DOM节点加载完毕的情况,如果直接调用初始化函数,可能会因为依赖未就绪导致功能异常。MutationObserver可以监听DOM树的变化,帮助我们精准判断依赖条件是否满足,从而确保初始化函数在合适的时机执行。

MutationObserver基础介绍
MutationObserver是浏览器提供的用于监听DOM树变动的接口,当被监听的DOM节点发生变化时,会触发回调函数,我们可以根据不同的变动类型执行对应的逻辑。它相比传统的DOMNodeInserted等事件,性能更优,也不会产生冒泡问题。
核心使用步骤
- 创建MutationObserver实例,传入变动触发时的回调函数
- 配置需要监听的变动类型,比如子节点增减、属性变化等
- 调用实例的observe方法,指定要监听的目标节点和配置项
- 当条件满足后,调用实例的disconnect方法停止监听,避免无用性能消耗
实现脚本执行完毕后执行初始化函数的场景
假设我们有一个第三方脚本会在执行完成后向页面中插入一个id为target的DOM节点,我们的初始化函数需要操作这个节点,如果直接执行初始化函数,很可能此时target节点还未被插入,导致报错。这时候就可以用MutationObserver监听body节点的子节点变化,等待target节点出现后再执行初始化函数。
完整实现代码
// 初始化函数,需要操作target节点
function init() {
const target = document.getElementById('target');
if (target) {
console.log('初始化函数执行,target节点内容:', target.innerHTML);
// 这里写具体的初始化逻辑
}
}
// 判断target节点是否已经存在,如果存在直接执行初始化
if (document.getElementById('target')) {
init();
} else {
// 创建MutationObserver实例
const observer = new MutationObserver((mutationsList) => {
// 遍历所有变动记录
for (const mutation of mutationsList) {
// 判断是否是子节点新增的变动
if (mutation.type === 'childList') {
// 检查新增的节点中是否有target节点
const addedNodes = mutation.addedNodes;
for (const node of addedNodes) {
if (node.id === 'target') {
// 找到目标节点,执行初始化函数
init();
// 停止监听,避免重复触发
observer.disconnect();
break;
}
}
}
}
});
// 配置监听规则:监听子节点的增减变化
const config = {
childList: true,
subtree: true // 监听所有后代节点的变化
};
// 开始监听body节点的变化
observer.observe(document.body, config);
}
监听脚本加载完成后再执行初始化
如果初始化函数依赖某个脚本执行完成后的全局变量,我们可以监听脚本标签的加载完成事件,同时结合MutationObserver监听脚本标签的状态变化,确保脚本完全执行后再触发初始化。
示例代码
// 假设依赖的脚本会暴露全局变量depData
function init() {
if (window.depData) {
console.log('依赖数据已就绪,执行初始化:', window.depData);
// 初始化逻辑
}
}
// 动态加载脚本的函数
function loadScript(src, callback) {
const script = document.createElement('script');
script.src = src;
script.onload = () => {
// 脚本加载完成后执行回调
callback();
};
document.head.appendChild(script);
}
// 检查全局变量是否已经存在
if (window.depData) {
init();
} else {
// 监听head节点的子节点变化,等待脚本标签加载完成
const observer = new MutationObserver((mutations) => {
for (const mutation of mutations) {
if (mutation.type === 'childList') {
const addedNodes = mutation.addedNodes;
for (const node of addedNodes) {
// 如果是脚本标签且已经加载完成
if (node.tagName === 'SCRIPT' && node.readyState === 'complete') {
// 检查依赖变量是否存在
if (window.depData) {
init();
observer.disconnect();
break;
}
}
}
}
}
});
observer.observe(document.head, {
childList: true,
subtree: true
});
// 加载依赖脚本
loadScript('https://ipipp.com/dep.js', () => {
// 脚本加载完成后再次检查,避免监听遗漏
if (window.depData) {
init();
observer.disconnect();
}
});
}
注意事项
- 使用MutationObserver后,在条件满足时一定要调用
disconnect方法停止监听,否则会持续消耗性能 - 配置监听规则时,
subtree参数如果设置为true,会监听目标节点的所有后代节点,根据实际场景选择是否开启,避免不必要的监听范围 - 如果目标节点在初始化前可能被多次插入,需要在初始化逻辑中增加判断,避免重复执行初始化函数
- 低版本浏览器可能不支持MutationObserver,如果需要兼容可以搭配传统的setTimeout轮询方案作为降级处理
MutationObserver初始化函数脚本执行dom_监听修改时间:2026-06-16 12:39:25