Навигационные хуки

Как следует из названия, навигационные хуки vue-router используются для перенаправлений или отмены навигационных переходов. Есть несколько способов внедрить навигационный хук: глобально, для конкретного маршрута, или для конкретного компонента.

Следует помнить, что изменение параметров маршрута не вызывает выполнения навигационных хуков enter/leave. Вы можете добавить watch на объект $route для отслеживания этих изменений, или использовать хук beforeRouteUpdate.

Глобальные хуки (до навигационных хуков)

Глобальный хук можно зарегистрировать через router.beforeEach:

const router = new VueRouter({ ... })

router.beforeEach((to, from, next) => {
  // ...
})

Глобальные навигационные хуки вызываются в порядке их создания при каждом навигационном переходе. Допускается асинхронное разрешение хуков — в этом случае переход считается незавершённым до тех пор, пока не будут разрешены все хуки.

В каждый навигационный хук передаётся три параметра:

  • to: Route: целевой объект Route, к которому осуществляется переход.

  • from: Route: текущий маршрут, с которого осуществляется переход к новому.

  • next: Function: функция, вызов которой разрешает хук. В зависимости от переданных в next аргументов, результатом будет:

    • next(): переход к следующему хуку в цепочке. Если хуков больше нет, переход считается подтверждённым.

    • next(false): отмена перехода. Если URL был изменён (вручную пользователем, или кнопкой "назад"), он будет сброшен на соответствующий маршрут from.

    • next('/') или next({ path: '/' }): перенаправление на другой маршрут. Текущий переход будет отменён, и процесс начнётся заново для нового маршрута. Вы можете передать любой объект местоположения в next, который позволяет вам указывать опции такие как replace: true, name: 'home' и любой другой параметр используемый во входном параметре to компонента router-link или router.push

    • next(error): (добавлено в версии 2.4.0+) если аргумент, переданный next является экземпляром Error, навигация будет прервана и ошибка будет передана в коллбэк, зарегистрированный через router.onError().

Убедитесь, что функция next будет вызываться в навигационном хуке только 1 раз в любом случае. Вызовы могут встречаться несколько раз, но важно чтобы они не пересекались логически, иначе хук никогда не разрешится или выдаст ошибки. Вот пример перенаправления пользователя на страницу /login если он не авторизован:

// ПЛОХО
router.beforeEach((to, from, next) => {
  if (to.name !== 'Login' && !isAuthenticated) next({ name: 'Login' })
  // если пользователь не авторизован, то `next` будет вызываться дважды
  next()
})
// ХОРОШО
router.beforeEach((to, from, next) => {
  if (to.name !== 'Login' && !isAuthenticated) next({ name: 'Login' })
  else next()
})

Глобальные хуки разрешения перехода

Вы можете зарегистрировать глобальный хук с помощью router.beforeResolve. Это похоже на router.beforeEach, с той разницей, что разрешающий хук будет вызван непосредственно перед подтверждением навигации, после того, как будут разрешены все хуки компонента и асинхронные компоненты для маршрута.

Глобальные хуки завершения перехода

Можно также зарегистрировать глобальные хуки, вызываемые после завершения перехода. Однако, в отличие от сторожевых хуков, в них не передаётся функция next, и на навигацию они повлиять не могут:

router.afterEach((to, from) => {
  // ...
})

Хуки для конкретных маршрутов

Навигационные хуки beforeEnter можно указать напрямую для конкретного маршрута в его конфигурации:

const router = new VueRouter({
  routes: [
    {
      path: '/foo',
      component: Foo,
      beforeEnter: (to, from, next) => {
        // ...
      }
    }
  ]
})

Эти хуки имеют точно такую же сигнатуру, как и глобальные хуки.

Хуки для конкретных компонентов

Наконец, навигационный хук можно указать и непосредственно в компоненте (том, что указан в конфигурации маршрута), используя следующие опции:

  • beforeRouteEnter
  • beforeRouteUpdate
  • beforeRouteLeave
const Foo = {
  template: `...`,
  beforeRouteEnter (to, from, next) {
    // вызывается до подтверждения пути, соответствующего этому компоненту.
    // НЕ ИМЕЕТ доступа к контексту экземпляра компонента `this`,
    // так как к моменту вызова экземпляр ещё не создан!
  },
  beforeRouteUpdate (to, from, next) {
    // вызывается когда маршрут, что рендерит этот компонент изменился,
    // но этот компонент будет повторно использован в новом маршруте.
    // Например, для маршрута с динамическими параметрами `/foo/:id`, когда мы
    // перемещаемся между `/foo/1` и `/foo/2`, экземпляр того же компонента `Foo`
    // будет использован повторно, и этот хук будет вызван когда это случится.
    // Также имеется доступ в `this` к экземпляру компонента.
  },
  beforeRouteLeave (to, from, next) {
    // вызывается перед переходом от пути, соответствующего текущему компоненту;
    // имеет доступ к контексту экземпляра компонента `this`.
  }
}

Хук beforeRouteEnter НЕ ИМЕЕТ доступа к this, так как к моменту его вызова навигация ещё не подтверждена, а значит и экземпляр компонента ещё не создан.

Тем не менее, доступ к экземпляру можно получить, передав коллбэк в next. Эта функция будет вызвана после подтверждения навигации, а экземпляр компонента будет передан в неё в качестве параметра:

beforeRouteEnter (to, from, next) {
  next(vm => {
    // экземпляр компонента доступен как `vm`
  })
}

Обратите внимание, что beforeRouteEnter — единственный хук, который поддерживает передачу коллбэка в next. Для beforeRouteUpdate и beforeRouteLeave, this уже доступен, поэтому передача коллбэка не требуется и поэтому не поддерживается:

beforeRouteUpdate (to, from, next) {
  // просто используйте `this`
  this.name = to.params.name
  next()
}

Навигационный хук ухода со страницы обычно используется для предотвращения случайного ухода пользователя со страницы с несохранёнными изменениями. Навигацию можно отменить вызовом next(false).

beforeRouteLeave (to, from, next) {
  const answer = window.confirm('Вы хотите уйти? У вас есть несохранённые изменения!')
  if (answer) {
    next()
  } else {
    next(false)
  }
}

Полная цепочка обработки навигации

  1. Срабатывание навигации.
  2. Вызов beforeRouteLeave хуков в деактивируемых компонентах.
  3. Вызов глобальных beforeEach хуков.
  4. Вызов beforeRouteUpdate хука в переиспользуемых компонентах.
  5. Вызов beforeEnter в конфигурации маршрута.
  6. Разрешение асинхронных компонентов для маршрута.
  7. Вызов beforeRouteEnter в активируемых компонентах.
  8. Вызов глобальных beforeResolve хуков.
  9. Навигация подтверждена.
  10. Вызов глобальных afterEach хуков.
  11. Выполняется обновление DOM.
  12. Вызов коллбэков, переданных в next в beforeRouteEnter хуке с созданными экземплярами.