What causes this vue-router async component error - TypeError: undefined is not an object (evaluating 't.__esModule')?
What causes this vue-router async component error - TypeError: undefined is not an object (evaluating 't.__esModule')?
我注意到 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)上发生。
我已经尝试使用谷歌搜索错误,但我似乎无法在其他任何地方找到对它的单一引用。我也无法 post 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(),
},
});
});
}
它应该显示一个允许用户在块加载失败时刷新页面的 toast。
但是我们忘记将错误传递给 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
处理程序中的错误可解决问题。
我注意到 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)上发生。
我已经尝试使用谷歌搜索错误,但我似乎无法在其他任何地方找到对它的单一引用。我也无法 post 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(),
},
});
});
}
它应该显示一个允许用户在块加载失败时刷新页面的 toast。
但是我们忘记将错误传递给 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
处理程序中的错误可解决问题。