File: navigation-guards.md

package info (click to toggle)
vue-router.js 3.4.9%2Bds-2
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 3,212 kB
  • sloc: javascript: 7,982; sh: 22; makefile: 5
file content (169 lines) | stat: -rw-r--r-- 11,234 bytes parent folder | download
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` хуке с созданными экземплярами.