Что вызывает эту ошибку асинхронного компонента vue-router — TypeError: undefined не является объектом (оценка «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).

Я попытался погуглить ошибку, и я не могу найти ни одной ссылки на нее где-либо еще. Я также не могу опубликовать проблему на Github vue-router, потому что я не могу предоставить минимальное воспроизведение.

Часто (но не всегда) 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.

Что вызывает эту ошибку?


person Maros    schedule 14.02.2020    source источник


Ответы (1)


Я разветвил библиотеку 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, кажется, загружает эти компоненты с помощью инициатора, называемого начальной загрузкой:

загрузка компонента asycn

Затем мне пришло в голову, что мы используем следующую оболочку для каждого импорта асинхронного компонента в 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 устраняет проблему.

????

person Maros    schedule 18.02.2020