背景
我在升级Vue2
的项目依赖及开发工具从npm
切换至pnpm
过程中,当重新运行pnpm run serve
出现了令人困扰的运行时错误。
问题
Uncaught runtime errors: ×
ERROR
ResizeObserver loop completed with undelivered notifications. at handleError (webpack-internal:///./node_modules/.pnpm/webpack-dev-server@4.15.2_webpack@5.94.0/node_modules/webpack-dev-server/client/overlay.js:252:58) at eval (webpack-internal:///./node_modules/.pnpm/webpack-dev-server@4.15.2_webpack@5.94.0/node_modules/webpack-dev-server/client/overlay.js:271:7)
那么怎么解决呢???
首先我肯定搜索项目代码中是否有存在关键字ResizeObserver
,我在源代码里并没有发现哦!
但是我在一个异步远程加载打包的组件产物里找到了(如下图)。
为什么产物里有
ResizeObserver
和源代码里没有?这里我没有深入去研究,以下是一些可能的原因。
Third-Party Libraries or Dependencies
Webpack Transpilation
Auto-Injected Polyfills
Dynamic Imports and Code Splitting
Summary: The
ResizeObserver
might be coming from third-party libraries, Webpack’s polyfill behavior, or dynamically loaded modules. To resolve this, inspect your dependencies, tweak your Babel configuration, and investigate how Webpack is bundling the app.
我在解决这个问题的思路历程大致如下:
1. 直接通过搜索引擎譬如Chrome
或者百度搜索的解决方案:
以上方案基本上不约而同的使用了防抖函数(如下图所示)
但是我尝试了没有用哦!
2. 通过ChatGPT
提供的解决方案:
1.它也提供了类似防抖函数debounce的方案,如下面代码所示
但是我刷新错误依然无法解决哦!
// 1. **debounced-observer.js**: Use `requestAnimationFrame` and throttle updates
const debounce = (fn, delay) => {
let timer = null;
return function () {
const context = this;
const args = arguments;
clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(context, args);
}, delay);
};
};
// Throttle function using requestAnimationFrame
const throttleWithRaf = (fn) => {
let isRequestPending = false;
return function () {
if (isRequestPending) return;
isRequestPending = true;
requestAnimationFrame(() => {
fn.apply(this, arguments);
isRequestPending = false;
});
};
};
// Create a debounced ResizeObserver class with raf-throttled callback
class DebouncedResizeObserver {
constructor(callback, delay = 16) {
this.observer = new ResizeObserver(throttleWithRaf(debounce(callback, delay)));
}
observe(...args) {
this.observer.observe(...args);
}
unobserve(...args) {
this.observer.unobserve(...args);
}
disconnect() {
this.observer.disconnect();
}
}
export default DebouncedResizeObserver;
// 2. **In App.vue**: Use the `DebouncedResizeObserver`
<script>
import DebouncedResizeObserver from '@/utils/debounced-observer';
export default {
name: 'App',
mounted() {
// Create a new debounced observer instance with raf-throttling
this.resizeObserver = new DebouncedResizeObserver((entries) => {
entries.forEach(entry => {
console.log(entry.target.getBoundingClientRect());
});
});
const element = this.$refs.resizableElement;
if (element) {
// Observe the element
this.resizeObserver.observe(element);
}
},
beforeDestroy() {
// Clean up observer on component destroy
if (this.resizeObserver) {
this.resizeObserver.disconnect();
}
}
}
</script>
<template>
<div ref="resizableElement">
<!-- Your resizable content goes here -->
</div>
</template>
2.通过
Adding an Overlay
来配置vue.config.js
,如下面配置
// 这里就差一点点啊,没有给出runtimeErrors的配置,导致error依然无法消失
devServer: {
client: {
overlay: {
warnings: false,
errors: true,
},
},
}
接下来。。。
1.放弃防抖函数:由于以上方案,我尝试了很久,都没能阻止overlay
的Error
,随后开启了循环操作模式:尝试卸载依赖——>重新安装依赖——>不断重启项目——>胡乱验证,尽管如此依然没能解决问题,最后决定放弃了防抖函数的方案。
2.进一步跟随ChatGPT
的启示:即使ChatGPT
提供了很全面的思路参考,甚至代码范例,但它似乎总是和你绕口令似的,最后似乎总是差了那么一点,但是它依然是很好的助手,在前进的道路上指引着向前。
3.基于前期的努力,我想既然Error
是来自于webpack-dev-server/client/overlay.js
,那么问题是由它抛出来的,是不是可以通过配置或者通过源码找寻答案呢,于是我顺理成章的进入了如下的第三步。
3. 通过Webpack
文档的解决方案(webpack-dev-server/overlay)
通过在vue.config.js
打印console.log(error)
,如下的三张图所示,审查源码添加的打印日志,在匹配到相应的错误信息时,通过return false
成功临时关闭了overlay
的报错信息。
答案(配置runtimeErrors
,关闭特定的Error
)
只是一个简单配置的兜兜转转,最终做的是我们很多时候需要回归文档、始终遵循文档,这是一个解决问题的起点。
缺陷
这是一个开发环境下出现的Error
,通过配置runtimeErrors
,临时关闭特定的Error
,这是一个很不完美的方案,可能触发此Error
的原因来源于Bad
代码,这个需要不断验证,需要致力于编写稳健且性能好的代码。
参考
How to Migrate from Vue CLI to Vite - Vue School Articles
vue-cli-to-vite-migration-example
【Bug】ERROR ResizeObserver loop completed with undelivered notifications.-阿里云开发者社区
react-resizeobserver-loop-completed-with-undelivered-notifications