You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
@@ -9,32 +9,41 @@ You can register global before guards using `router.beforeEach`:
9
9
```js
10
10
constrouter=createRouter({ ... })
11
11
12
-
router.beforeEach((to, from, next) => {
12
+
router.beforeEach((to, from) => {
13
13
// ...
14
-
next()
14
+
// explicitly return false to cancel the navigation
15
+
returnfalse
15
16
})
16
17
```
17
18
18
19
Global before guards are called in creation order, whenever a navigation is triggered. Guards may be resolved asynchronously, and the navigation is considered **pending** before all hooks have been resolved.
19
20
20
-
Every guard function receives three arguments:
21
+
Every guard function receives two arguments:
21
22
22
-
-**`to`**: the target [Normalized RouteLocation](../../api/#the-route-object) being navigated to.
23
+
-**`to`**: the target route ___location [in a normalized format](../../api/#the-route-object) being navigated to.
24
+
-**`from`**: the current route ___location [in a normalized format](../../api/#the-route-object) being navigated away from.
23
25
24
-
-**`from`**: the current route ___location (same type as `to`) being navigated away from.
26
+
And can optionally return any of the following values:
25
27
26
-
-**`next`**: a function that **must be called to resolve** the hook. The action depends on the arguments provided to `next`:
28
+
-`false`: cancel the current navigation. If the browser URL was changed (either manually by the user or via back button), it will be reset to that of the `from` route.
29
+
- A [Route Location](../api#route-___location): Redirect to a different ___location by passing a route ___location as if you were calling [`router.push()`](../api#router-push), which allows you to pass options like `replace: true` or `name: 'home'`. The current navigation is dropped and a new one is created with the same `from`.
27
30
28
-
-**`next()`**: move on to the next hook in the pipeline. If no hooks are left, the navigation is **validated**.
31
+
It's also possible to throw an `Error` if an unexpected situation was met. This will also cancel the navigation and call any callback registered via [`router.onError()`](../api#router-onerror).
29
32
30
-
-**`next(false)`**: cancel the current navigation. If the browser URL was changed (either manually by the user or via back button), it will be reset to that of the `from` route.
33
+
If nothing, `undefined` or `true` is returned, **the navigation is validated**, and the next navigation guard is called.
31
34
32
-
-**`next('/')` or `next({ path: '/' })`**: redirect to a different ___location. The current navigation will be dropped and a new one will be initiated. **You can pass any route ___location object** to `next`, which allows you to specify options like `replace: true`, `name: 'home'`and any option used in [`router-link`'s `to` prop](../../api/#to) or [`router.push`](../../api/#router-push)
35
+
All of the the things above **work the same way with `async` functions**and Promises:
33
36
34
-
-**`next(error)`**: (2.4.0+) if the argument passed to `next` is an instance of `Error`, the navigation will be canceled and the error will be passed to callbacks registered via [`router.onError()`](../../api/#router-onerror). The same happens if an error is directly thrown.
37
+
```js
38
+
router.beforeEach(async (to, from) => {
39
+
// canUserAccess() returns `true` or `false`
40
+
returnawaitcanUserAccess(to)
41
+
})
42
+
```
43
+
44
+
### Optional third argument `next`
35
45
36
-
::: tip Note
37
-
For all guards, **make sure that the `next` function is called exactly once** in any given pass through the navigation guard. It can appear more than once, but only if the logical paths have no overlap, otherwise the hook will never be resolved or produce errors. Here is an example of redirecting to user to `/login` if they are not authenticated:
46
+
In previous versions of Vue Router, it was also possible to use a _third argument_`next`, this was a common source of mistakes and went through an [RFC](https://github.com/vuejs/rfcs/blob/master/active-rfcs/0037-router-return-guards.md#motivation) to remove it. However, it is still supported, meaning you can pass a third argument to any navigation guard. In that case, **you must call `next` exactly once** in any given pass through a navigation guard. It can appear more than once, but only if the logical paths have no overlap, otherwise the hook will never be resolved or produce errors. Here is **a bad example** of redirecting to user to `/login` if they are not authenticated:
You can register a global guard with `router.beforeResolve`. This is similar to `router.beforeEach` because it triggers on **every navigation**, but resolve guards are called right before the navigation is confirmed, **after all in-component guards and async route components are resolved**. This is the ideal spot to fetch data or do any other operation that you want to avoid doing if the user cannot enter a page. It's also very easy to combine with [`meta` fields](./meta.md) to create a [generic fetching mechanism](../../cookbook/generic-data-fetching.md):
69
+
You can register a global guard with `router.beforeResolve`. This is similar to `router.beforeEach` because it triggers on **every navigation**, but resolve guards are called right before the navigation is confirmed, **after all in-component guards and async route components are resolved**. Here is an example that ensures the user has given access to the Camera for routes that [have defined a custom meta](#TODO) property `requiresCamera`:
61
70
62
71
```js
63
-
router.beforeResolve(async(to, from, next)=> {
72
+
router.beforeResolve(asyncto=> {
64
73
if (to.meta.requiresCamera) {
65
74
try {
66
75
awaitaskForCameraPermission()
67
-
next()
68
76
} catch (error) {
69
77
if (error instanceof NotAllowedError) {
70
78
// ... handle the error and then cancel the navigation
71
-
next(false)
79
+
returnfalse
72
80
} else {
73
81
// unexpected error, cancel the navigation and pass the error to the global handler
74
-
next(error)
82
+
throwerror
75
83
}
76
84
}
77
-
} else {
78
-
// make sure to always call `next`
79
-
next()
80
85
}
81
86
})
82
87
```
83
88
89
+
`router.beforeResolve` is the ideal spot to fetch data or do any other operation that you want to avoid doing if the user cannot enter a page. It's also very easy to combine with [`meta` fields](./meta.md) to create a [generic fetching mechanism](../../cookbook/generic-data-fetching.md)
90
+
84
91
## Global After Hooks
85
92
86
93
You can also register global after hooks, however unlike guards, these hooks do not get a `next` function and cannot affect the navigation:
They are useful for analytics, [changing the title of the page](../../cookbook/page-title.md), [accessibility](../../cookbook/announcing-navigation.md) and many other things.
95
102
103
+
They also reflect [navigation failures](./navigation-failures.md) as the third argument:
104
+
105
+
```js
106
+
router.afterEach((to, from, failure) => {
107
+
if (!failure) sendToAnalytics(to.fullPath)
108
+
})
109
+
```
110
+
111
+
Learn more about navigation failures on [its guide](./navigation-failures.md).
112
+
96
113
## Per-Route Guard
97
114
98
115
You can define `beforeEnter` guards directly on a route's configuration object:
@@ -102,19 +119,51 @@ const routes = [
102
119
{
103
120
path:'/users/:id',
104
121
component: UserDetails,
105
-
beforeEnter: (to, from, next) => {
106
-
//...
107
-
next()
108
-
}
109
-
}
122
+
beforeEnter: (to, from) => {
123
+
//reject the navigation
124
+
returnfalse
125
+
},
126
+
},
110
127
]
111
128
```
112
129
113
130
`beforeEnter` guards **only trigger when entering the route**, they don't trigger when the `params`, `query` or `hash` change e.g. going from `/users/2` to `/users/3` or going from `/users/2#info` to `/users/2#projects`. They are only triggered when navigating **from a different** route.
114
131
132
+
You can also pass an array of functions to `beforeEnter`, this is useful when reusing guards for different routes:
133
+
134
+
```js
135
+
functionremoveQueryParams(to) {
136
+
if (Object.keys(to.query).length)
137
+
return { path:to.path, query: {}, hash:to.hash }
138
+
}
139
+
140
+
functionremoveHash(to) {
141
+
if (to.hash) return { path:to.path, query:to.query, hash:'' }
142
+
}
143
+
144
+
constroutes= [
145
+
{
146
+
path:'/users/:id',
147
+
component: UserDetails,
148
+
beforeEnter: [removeQueryParams, removeHash],
149
+
},
150
+
{
151
+
path:'/about',
152
+
component: UserDetails,
153
+
beforeEnter: [removeQueryParams],
154
+
},
155
+
]
156
+
```
157
+
158
+
Note it is possible to achieve a similar behavior by using [route meta fields](./meta.md) and [global navigation guards](#global-before-guards).
159
+
115
160
## In-Component Guards
116
161
117
-
Finally, you can directly define route navigation guards inside route components (the ones passed to the router configuration) with the following options:
162
+
Finally, you can directly define route navigation guards inside route components (the ones passed to the router configuration)
163
+
164
+
### Using the options API
165
+
166
+
You can add the following options to route components:
118
167
119
168
-`beforeRouteEnter`
120
169
-`beforeRouteUpdate`
@@ -123,24 +172,24 @@ Finally, you can directly define route navigation guards inside route components
123
172
```js
124
173
constUserDetails= {
125
174
template:`...`,
126
-
beforeRouteEnter(to, from, next) {
175
+
beforeRouteEnter(to, from) {
127
176
// called before the route that renders this component is confirmed.
128
177
// does NOT have access to `this` component instance,
129
178
// because it has not been created yet when this guard is called!
130
179
},
131
-
beforeRouteUpdate(to, from, next) {
180
+
beforeRouteUpdate(to, from) {
132
181
// called when the route that renders this component has changed,
133
182
// but this component is reused in the new route.
134
183
// For example, given a route with params `/users/:id`, when we
135
184
// navigate between `/users/1` and `/users/2`, the same `UserDetails` component instance
136
185
// will be reused, and this hook will be called when that happens.
137
186
// Because the component is mounted while this happens, the navigation guard has access to `this` component instance.
138
187
},
139
-
beforeRouteLeave(to, from, next) {
188
+
beforeRouteLeave(to, from) {
140
189
// called when the route that renders this component is about to
141
190
// be navigated away from.
142
191
// As with `beforeRouteUpdate`, it has access to `this` component instance.
Note that `beforeRouteEnter` is the only guard that supports passing a callback to `next`. For `beforeRouteUpdate` and `beforeRouteLeave`, `this` is already available, so passing a callback is unnecessary and therefore _not supported_:
160
209
161
210
```js
162
-
beforeRouteUpdate (to, from, next) {
211
+
beforeRouteUpdate (to, from) {
163
212
// just use `this`
164
213
this.name=to.params.name
165
-
next()
166
214
}
167
215
```
168
216
169
-
The **leave guard** is usually used to prevent the user from accidentally leaving the route with unsaved edits. The navigation can be canceled by calling `next(false)`.
217
+
The **leave guard** is usually used to prevent the user from accidentally leaving the route with unsaved edits. The navigation can be canceled by returning `false`.
170
218
171
219
```js
172
-
beforeRouteLeave (to, from, next) {
220
+
beforeRouteLeave (to, from) {
173
221
constanswer=window.confirm('Do you really want to leave? you have unsaved changes!')
174
-
if (answer) {
175
-
next()
176
-
} else {
177
-
next(false)
178
-
}
222
+
if (!answer) returnfalse
223
+
}
224
+
```
225
+
226
+
### Using the composition API
227
+
228
+
If you are writing your component using the [composition API and a `setup` function](https://v3.vuejs.org/guide/composition-api-setup.html#setup), you can add update and leave guards:
0 commit comments