我正在尝试制作一个 chrome 扩展,它将向 React Web 应用程序注入内容脚本。虽然我成功地注入了代码,但页面的延迟Hydration经常会覆盖脚本所做的更改。
可以通过设置任意超时来在一定程度上避免这个问题,但这几乎不能算是解决方案,因为水合速度以及所需的时间因设备而异。
有没有办法检测页面何时完成水合作用,最好是通过承诺,以便我可以在之后对页面进行更改?
附注我已经尝试过
customeElements.whenDefined()
但 DevTools 一直告诉我 customElements
未定义。
我提出的一个潜在解决方案涉及使用 MutationObserver 来监视 DOM 更改并将其与去抖函数结合起来。这个想法是,如果在指定的时间范围内(例如 1 秒)没有检测到任何变化,则水合作用可能已完成。
示例:
const delay = 100;
let debounceTimeout: NodeJS.Timeout;
const timeoutCallback = () => {
observer.disconnect(); // Stop observing
console.log("Hydration probably completed.");
// execute your code here
};
const observer = new MutationObserver((mutations, obs) => {
console.log("Mutation observed", mutations, obs);
clearTimeout(debounceTimeout);
debounceTimeout = setTimeout(timeoutCallback, delay); // No mutations have occurred for the specified delay;
});
debounceTimeout = setTimeout(timeoutCallback, delay); // if no mutations occur at all (e.g. if the page is already loaded)
observer.observe(document.body, {
subtree: true,
childList: true,
attributes: false, // DO NOT set this to true because your page could have some dynamic component with some cool css animation written in react, and then you will get infinite amount of calls
});
或者您可以将其移至单独的功能 像这样
export function observeDomStabilization(
cb: () => void,
{
delay = 100,
observeTarget,
observeConfig,
}: {
delay?: number;
observeTarget: Node;
observeConfig: MutationObserverInit;
}
) {
let debounceTimeout: NodeJS.Timeout;
const timeoutCallback = () => {
observer.disconnect(); // Stop observing
cb(); // Execute callback
};
const observer = new MutationObserver((mutations, obs) => {
console.log("Mutation observed", mutations, obs);
clearTimeout(debounceTimeout);
debounceTimeout = setTimeout(timeoutCallback, delay); // No mutations have occurred for the specified delay;
});
debounceTimeout = setTimeout(timeoutCallback, delay); // if no mutations occur at all
observer.observe(observeTarget, observeConfig);
}
然后像这样使用它
observeDomStabilization(
async () => {
console.log("Hydration likely completed.");
// mount some cool apps or inject dom elements
},
{
delay: 100,
observeTarget: document.body,
observeConfig: {
childList: true,
subtree: true,
},
}
);
我不确定这是否是最好的解决方案,但至少它有效(我希望如此)