我注意到以下堆栈跟踪中的Sentry中有很多错误:
TypeError: undefined is not an object (evaluating 't.__esModule')
at isESModule(./node_modules/vue-router/dist/vue-router.esm.js:1955:3)
at ? (./node_modules/vue-router/dist/vue-router.esm.js:1882:27)
at promiseReactionJob([native code])
我在重现错误并弄清楚是什么原因时遇到了很多麻烦。查看vue-router源,它来自此功能:
function isESModule (obj) {
return obj.__esModule || (hasSymbol && obj[Symbol.toStringTag] === 'Module')
}
因此obj
未定义。如果我们上一级,我们将获得以下功能:
function resolveAsyncComponents (matched) {
return function (to, from, next) {
var hasAsync = false;
var pending = 0;
var error = null;
flatMapComponents(matched, function (def, _, match, key) {
// if it's a function and doesn't have cid attached,
// assume it's an async component resolve function.
// we are not using Vue's default async resolving mechanism because
// we want to halt the navigation until the incoming component has been
// resolved.
if (typeof def === 'function' && def.cid === undefined) {
hasAsync = true;
pending++;
var resolve = once(function (resolvedDef) {
if (isESModule(resolvedDef)) {
resolvedDef = resolvedDef.default;
}
// save resolved on async factory in case it's used elsewhere
def.resolved = typeof resolvedDef === 'function'
? resolvedDef
: _Vue.extend(resolvedDef);
match.components[key] = resolvedDef;
pending--;
if (pending <= 0) {
next();
}
});
...
因此resolvedDef
似乎未定义。
我的猜测是Vue路由器成功解析了一个异步组件,但最终未定义,并且代码中没有任何内容可以解决这种情况。
我正在使用vue-router 3.1.3,并且这些错误始终仅在iOS(Safari或Chrome)上发生。
我已经尝试使用Google搜索错误,但似乎在其他任何地方都找不到该错误的单个引用。我也无法在vue-router Github上发布问题,因为我无法提供最低限度的复制。
通常(但并非总是)Sentry还会在错误之前显示此控制台日志:
console [vue-analytics] An error occured! Please check your connection or disable your AD blocker
logger console
extra {"arguments":["[vue-analytics] An error occured! Please check your connection or disable your AD blocker"]}
我不确定这是否相关。
我正在考虑的一个疯狂的解决方案是部署了vue-router的修补版本,在未定义resolvedDef
的情况下,它会在更有用的上下文中引发错误。该错误有望在我们的Sentry日志中结束。
是什么导致此错误?
我派生了vue-router库,并在此行中修补了resolveAsyncComponents
:
if (!resolvedDef) {
window.onerror(`router wtf ${matched.join(',')} ${def} ${_} ${match} ${key} ${pending} ${hasAsync} ${error}`);
}
最终在Sentry中出现以下错误:
router wtf [object Object] function(){return u(Promise.all([n.e("dashboard~orders~products~publicUser"),n.e("dashboard~discounts~products~publicUser"),n.e("orders~publicUser~tips"),n.e("publicUser~tips"),n.e("publicUser")]).then(n.bind(null,"89c6")))...
在生产中,我们的Vue应用程序似乎使用名为bootstrap的启动程序加载了这些组件:
然后令我震惊的是,我们在router.js
中的每个异步组件导入中使用以下包装:
{ path: 'dashboard', name: 'dashboard', component: () => handleChunkLoadError( import(/* webpackChunkName: 'dashboard' */ '../views/Dashboard') ), },
function handleChunkLoadError(importPromise) { return importPromise.catch(() => { Vue.toasted.error('Network error encountered', { duration: null, action: { text: 'Reload', onClick: () => window.location.reload(), }, }); }); }
应该显示一个吐司,如果块加载失败,该吐司将允许用户刷新页面。
但是我们忘记了将错误传递给vue-router:
function handleChunkLoadError(importPromise) { return importPromise.catch(error => { Vue.toasted.error('Network error encountered', { duration: null, action: { text: 'Reload', onClick: () => window.location.reload(), }, }); return error; }); }
返回
.catch
处理程序中的错误已解决此问题。
🤦