1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
|
# Навигационные хуки
Как следует из названия, навигационные хуки `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)
}
}
```
## Полная цепочка обработки навигации
1. Срабатывание навигации.
2. Вызов `beforeRouteLeave` хуков в деактивируемых компонентах.
3. Вызов глобальных `beforeEach` хуков.
4. Вызов `beforeRouteUpdate` хука в переиспользуемых компонентах.
5. Вызов `beforeEnter` в конфигурации маршрута.
6. Разрешение асинхронных компонентов для маршрута.
7. Вызов `beforeRouteEnter` в активируемых компонентах.
8. Вызов глобальных `beforeResolve` хуков.
9. Навигация подтверждена.
10. Вызов глобальных `afterEach` хуков.
11. Выполняется обновление DOM.
12. Вызов коллбэков, переданных в `next` в `beforeRouteEnter` хуке с созданными экземплярами.
|