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

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

Следует помнить, что **изменение параметров маршрута не вызывает выполнения навигационных хуков enter/leave**. Вы можете добавить [watch на объект `$route`](../essentials/dynamic-matching.md#отсnеживание-изменений-параметров) для отслеживания этих изменений, или использовать хук `beforeRouteUpdate`.

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

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

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

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

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

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

- **`to: Route`**: целевой [объект Route](../../api/#объект-route), к которому осуществляется переход.

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

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

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

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

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

  - **`next(error)`**: (добавлено в версии 2.4.0+) если аргумент, переданный `next` является экземпляром `Error`, навигация будет прервана и ошибка будет передана в коллбэк, зарегистрированный через [`router.onError()`](../../api/#router-onerror).

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

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

```js
// ХОРОШО
router.beforeEach((to, from, next) => {
  if (to.name !== 'Login' && !isAuthenticated) next({ name: 'Login' })
  else next()
})
```

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

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

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

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

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

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

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

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

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

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

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

- `beforeRouteEnter`
- `beforeRouteUpdate`
- `beforeRouteLeave`

```js
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`. Эта функция будет вызвана после подтверждения навигации, а экземпляр компонента будет передан в неё в качестве параметра:

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

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

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

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

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

При использовании примесей, которые будут добавлять навигационные хуки для компонента, убедитесь, что объявляете примесь **после установки плагина vue-router**:

```js
Vue.use(Router)

Vue.mixin({
  beforeRouteUpdate(to, from, next) {
    // ...
  }
})
```

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

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